mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-05 03:21:42 +00:00
Compare commits
182 Commits
jenkins-re
...
jenkins-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
964475c724 | ||
|
|
bfa4bbaf78 | ||
|
|
39158754d7 | ||
|
|
048b02e6cd | ||
|
|
130319b771 | ||
|
|
0b414b1bde | ||
|
|
8a244cc9dc | ||
|
|
397162390b | ||
|
|
bbc216f998 | ||
|
|
72a56e6a8a | ||
|
|
102c992e50 | ||
|
|
9f057fddfd | ||
|
|
e614217d41 | ||
|
|
9b7042a51a | ||
|
|
081d7f7361 | ||
|
|
352c021b17 | ||
|
|
5f90bf167e | ||
|
|
445eafefb3 | ||
|
|
a917f63e7d | ||
|
|
2228361f39 | ||
|
|
aaa599fa28 | ||
|
|
5d95bbb33f | ||
|
|
d0258923e8 | ||
|
|
91ccc5d10b | ||
|
|
9fda920055 | ||
|
|
e9dd72ac77 | ||
|
|
cce1bbc1f2 | ||
|
|
9fc79c41d1 | ||
|
|
1dba916887 | ||
|
|
ce7a56284f | ||
|
|
34d515debb | ||
|
|
cff103d15e | ||
|
|
1b5cabc0d1 | ||
|
|
9fcfc5b3b7 | ||
|
|
f181c70134 | ||
|
|
46c40da539 | ||
|
|
1c05a982e1 | ||
|
|
ba7f56833b | ||
|
|
7600473da6 | ||
|
|
9f04b79580 | ||
|
|
2282a11a79 | ||
|
|
1a2344bddc | ||
|
|
ae9a7d19f1 | ||
|
|
e1141b9f27 | ||
|
|
ff71aca572 | ||
|
|
837671e95f | ||
|
|
38c0308fc7 | ||
|
|
f21e4a9da5 | ||
|
|
e13720bb94 | ||
|
|
0cf7d6f457 | ||
|
|
a2dfffeddc | ||
|
|
87e1df068c | ||
|
|
f19c290fde | ||
|
|
5c342a8e1a | ||
|
|
f7117aac4b | ||
|
|
05cd0627b2 | ||
|
|
2bc83bd80a | ||
|
|
79fbbbc0dd | ||
|
|
359a155915 | ||
|
|
fb7f5e9de8 | ||
|
|
7db63055f6 | ||
|
|
b94341a23e | ||
|
|
afbd70d393 | ||
|
|
ceccd95084 | ||
|
|
91b9812482 | ||
|
|
0dbc40e515 | ||
|
|
5ce89b3382 | ||
|
|
8ccf3d9c14 | ||
|
|
230fa2d168 | ||
|
|
d165f57e6e | ||
|
|
2d15b6b03e | ||
|
|
5d9baa44ee | ||
|
|
822416d2f7 | ||
|
|
3fe726d851 | ||
|
|
4c0b2c18ab | ||
|
|
b4c5a3af77 | ||
|
|
eddb0b20b0 | ||
|
|
6d8e782f53 | ||
|
|
0368322385 | ||
|
|
19e05ff52b | ||
|
|
a6aef54ef4 | ||
|
|
32f3bea465 | ||
|
|
9aacf5eb29 | ||
|
|
f3484686c9 | ||
|
|
b78b14bf5f | ||
|
|
c08dc98650 | ||
|
|
9fbffd88cb | ||
|
|
6c15a3600a | ||
|
|
548941dd99 | ||
|
|
9d8418c14e | ||
|
|
326bf4fc9c | ||
|
|
3c4a1d9a1a | ||
|
|
7687028269 | ||
|
|
a55f34646d | ||
|
|
53473e21be | ||
|
|
b4097fbd58 | ||
|
|
3b72114555 | ||
|
|
cc36454b90 | ||
|
|
aae20ddbff | ||
|
|
ffd9061766 | ||
|
|
9ffdea188b | ||
|
|
46dc99a115 | ||
|
|
91d714d2e9 | ||
|
|
9a28aaaa7c | ||
|
|
96a76ba89e | ||
|
|
d26059a4fb | ||
|
|
ee0d835304 | ||
|
|
6080a3b186 | ||
|
|
3d06a763a2 | ||
|
|
e1480ec798 | ||
|
|
a5d9ba412c | ||
|
|
2434515d41 | ||
|
|
741618250b | ||
|
|
8b8d7e77cd | ||
|
|
c093a553ee | ||
|
|
a1375e58cd | ||
|
|
15ff7f5038 | ||
|
|
c17ba98f72 | ||
|
|
dee755ab19 | ||
|
|
92c54f5f5d | ||
|
|
1fde00643f | ||
|
|
47443b4e1e | ||
|
|
f01e5b5570 | ||
|
|
198e2eed14 | ||
|
|
22c4207553 | ||
|
|
bea9eb0efa | ||
|
|
b72eb4b812 | ||
|
|
0d8c454727 | ||
|
|
d61d491a02 | ||
|
|
786e844a9f | ||
|
|
170b5860ee | ||
|
|
26c50ebe02 | ||
|
|
46c659d6b6 | ||
|
|
6fdd491081 | ||
|
|
fe4535dfa0 | ||
|
|
636e2e13ad | ||
|
|
d3f5b74668 | ||
|
|
8116bbd15b | ||
|
|
37052246a5 | ||
|
|
a649d3b553 | ||
|
|
6a7e7cf611 | ||
|
|
77997e52fb | ||
|
|
e655072efc | ||
|
|
0427fc34c4 | ||
|
|
e33d80be14 | ||
|
|
8381eee185 | ||
|
|
1c24096cc9 | ||
|
|
3a684d28b2 | ||
|
|
8786b242b2 | ||
|
|
b29a4bebf2 | ||
|
|
db0b421019 | ||
|
|
8efe998270 | ||
|
|
ac60198842 | ||
|
|
8a5ee71fd8 | ||
|
|
af4ce1074a | ||
|
|
7636041393 | ||
|
|
745489fec7 | ||
|
|
04f9ca4feb | ||
|
|
ca5dfbe492 | ||
|
|
07619a37a0 | ||
|
|
34d3d756ea | ||
|
|
61a5fcce18 | ||
|
|
82c4563d34 | ||
|
|
fa337bc747 | ||
|
|
8ae7e973f2 | ||
|
|
574f2e692a | ||
|
|
827341caa2 | ||
|
|
dd272e6bcb | ||
|
|
3bdaa63a28 | ||
|
|
41b393c210 | ||
|
|
11cf860ecd | ||
|
|
2168d2cb77 | ||
|
|
430722c4a3 | ||
|
|
497f38fe0e | ||
|
|
9f2dcdeab6 | ||
|
|
ac07142e4c | ||
|
|
19a7243bfc | ||
|
|
e3ac0b628c | ||
|
|
709a88ad68 | ||
|
|
6b844b52ec | ||
|
|
9056edf932 | ||
|
|
36c53667cd |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -10,6 +10,13 @@ bin/
|
||||
.project
|
||||
.classpath
|
||||
**/dependency-reduced-pom.xml
|
||||
wpilibj/wpilibJavaJNI/nivision/*.c
|
||||
wpilibj/wpilibJavaJNI/nivision/*.cpp
|
||||
wpilibj/wpilibJavaJNI/nivision/*.s
|
||||
wpilibj/wpilibJavaJNI/nivision/*_arm.ini
|
||||
wpilibj/wpilibJavaJNI/nivision/*.java
|
||||
wpilibj/wpilibJavaJNI/nivision/nivision_funcs.txt
|
||||
wpilibj/wpilibJavaJNI/nivision/imaqdx_funcs.txt
|
||||
|
||||
# Created by the jenkins test script
|
||||
test-reports
|
||||
@@ -155,6 +162,10 @@ local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
### Python ###
|
||||
*.pyc
|
||||
__pycache__
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
|
||||
@@ -85,6 +85,14 @@
|
||||
<outputDirectory>${tools-zip}</outputDirectory>
|
||||
<destFileName>SmartDashboard.jar</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>edu.wpi.first.wpilib</groupId>
|
||||
<artifactId>sfx</artifactId>
|
||||
<type>zip</type>
|
||||
<classifier>zip</classifier>
|
||||
<outputDirectory>${tools-zip}/../</outputDirectory>
|
||||
<destFileName>sfx.zip</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>edu.wpi.first.wpilib.networktables</groupId>
|
||||
<artifactId>OutlineViewer</artifactId>
|
||||
@@ -143,6 +151,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<unzip src="${tools-zip}/../sfx.zip" dest="${tools-zip}"/>
|
||||
<zip destfile="${project.build.directory}/classes/resources/tools.zip"
|
||||
basedir="${tools-zip}"
|
||||
update="true" />
|
||||
@@ -208,6 +217,12 @@
|
||||
<artifactId>SmartDashboard</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.wpilib</groupId>
|
||||
<artifactId>sfx</artifactId>
|
||||
<type>zip</type>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.wpilib.networktables</groupId>
|
||||
<artifactId>OutlineViewer</artifactId>
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RunSFXDashboardAction implements IWorkbenchWindowActionDelegate {
|
||||
* @see IWorkbenchWindowActionDelegate#run
|
||||
*/
|
||||
public void run(IAction action) {
|
||||
File dir = new File(WPILibCore.getDefault().getWPILibBaseDir()+File.separator+"tools");
|
||||
File dir = new File(WPILibCore.getDefault().getWPILibBaseDir()+File.separator+"tools"+File.separator+"sfx-2014.11.01");
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override public boolean accept(File dir, String name) {
|
||||
return name.startsWith("sfx") && name.endsWith(".jar");
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}}/src""/>
|
||||
<listOptionValue builtIn="false" value="${WPILIB}/cpp/current/sim/include"/>
|
||||
<listOptionValue builtIn="false" value="/usr/include"/>
|
||||
<listOptionValue builtIn="false" value="/usr/include/gazebo-3.1"/>
|
||||
<listOptionValue builtIn="false" value="/usr/include/gazebo-3.2"/>
|
||||
<listOptionValue builtIn="false" value="/usr/include/sdformat-2.2"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.optimization.level.1648211502" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
#include "WPILib.h"
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
* Example of finding yellow totes based on color.
|
||||
* This example utilizes an image file, which you need to copy to the roboRIO
|
||||
* To use a camera you will have to integrate the appropriate camera details with this example.
|
||||
* To use a USB camera instead, see the IntermediateVision example for details
|
||||
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
|
||||
* using an Axis Camera.
|
||||
*
|
||||
* Sample images can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
|
||||
*/
|
||||
class VisionColor2015Sample : public SampleRobot
|
||||
{
|
||||
//A structure to hold measurements of a particle
|
||||
struct ParticleReport {
|
||||
double PercentAreaToImageArea;
|
||||
double Area;
|
||||
double ConvexHullArea;
|
||||
double BoundingRectLeft;
|
||||
double BoundingRectTop;
|
||||
double BoundingRectRight;
|
||||
double BoundingRectBottom;
|
||||
};
|
||||
|
||||
//Structure to represent the scores for the various tests used for target identification
|
||||
struct Scores {
|
||||
double Trapezoid;
|
||||
double LongAspect;
|
||||
double ShortAspect;
|
||||
double AreaToConvexHullArea;
|
||||
};
|
||||
|
||||
//Images
|
||||
Image *frame;
|
||||
Image *binaryFrame;
|
||||
int imaqError;
|
||||
|
||||
//Constants
|
||||
Range TOTE_HUE_RANGE = {24, 49}; //Default hue range for yellow tote
|
||||
Range TOTE_SAT_RANGE = {67, 255}; //Default saturation range for yellow tote
|
||||
Range TOTE_VAL_RANGE = {49, 255}; //Default value range for yellow tote
|
||||
double AREA_MINIMUM = 2; //Default Area minimum for particle as a percentage of total image area
|
||||
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
|
||||
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
|
||||
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
|
||||
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
|
||||
ParticleFilterCriteria2 criteria[1];
|
||||
ParticleFilterOptions2 filterOptions = {0,0,1,1};
|
||||
Scores scores;
|
||||
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
// create images
|
||||
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
||||
binaryFrame = imaqCreateImage(IMAQ_IMAGE_U8, 0);
|
||||
|
||||
//Put default values to SmartDashboard so fields will appear
|
||||
SmartDashboard::PutNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Area min %", AREA_MINIMUM);
|
||||
}
|
||||
|
||||
void Autonomous() override {
|
||||
while (IsAutonomous() && IsEnabled())
|
||||
{
|
||||
//read file in from disk. For this example to run you need to copy image20.jpg from the SampleImages folder to the
|
||||
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
|
||||
imaqError = imaqReadFile(frame, "//home//lvuser//SampleImages//image20.jpg", NULL, NULL);
|
||||
|
||||
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
|
||||
TOTE_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
TOTE_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
TOTE_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
TOTE_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
TOTE_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
TOTE_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
|
||||
//Threshold the image looking for yellow (tote color)
|
||||
imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &TOTE_HUE_RANGE, &TOTE_SAT_RANGE, &TOTE_VAL_RANGE);
|
||||
|
||||
//Send particle count to dashboard
|
||||
int numParticles = 0;
|
||||
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
|
||||
SmartDashboard::PutNumber("Masked particles", numParticles);
|
||||
|
||||
//Send masked image to dashboard to assist in tweaking mask.
|
||||
SendToDashboard(binaryFrame, imaqError);
|
||||
|
||||
//filter out small particles
|
||||
float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
|
||||
criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
|
||||
imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);
|
||||
|
||||
//Send particle count after filtering to dashboard
|
||||
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
|
||||
SmartDashboard::PutNumber("Filtered particles", numParticles);
|
||||
|
||||
if(numParticles > 0)
|
||||
{
|
||||
//Measure particles and sort by particle size
|
||||
std::vector<ParticleReport> particles;
|
||||
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
|
||||
{
|
||||
ParticleReport par;
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_CONVEX_HULL_AREA, &(par.ConvexHullArea));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
|
||||
particles.push_back(par);
|
||||
}
|
||||
sort(particles.begin(), particles.end(), CompareParticleSizes);
|
||||
|
||||
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
|
||||
//for the reader. Note that the long and short side scores expect a single tote and will not work for a stack of 2 or more totes.
|
||||
//Modification of the code to accommodate 2 or more stacked totes is left as an exercise for the reader.
|
||||
scores.Trapezoid = TrapezoidScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Trapezoid", scores.Trapezoid);
|
||||
scores.LongAspect = LongSideScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Long Aspect", scores.LongAspect);
|
||||
scores.ShortAspect = ShortSideScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Short Aspect", scores.ShortAspect);
|
||||
scores.AreaToConvexHullArea = ConvexHullAreaScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Convex Hull Area", scores.AreaToConvexHullArea);
|
||||
bool isTote = scores.Trapezoid > SCORE_MIN && (scores.LongAspect > SCORE_MIN || scores.ShortAspect > SCORE_MIN) && scores.AreaToConvexHullArea > SCORE_MIN;
|
||||
bool isLong = scores.LongAspect > scores.ShortAspect;
|
||||
|
||||
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
|
||||
SmartDashboard::PutBoolean("IsTote", isTote);
|
||||
SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0), isLong));
|
||||
} else {
|
||||
SmartDashboard::PutBoolean("IsTote", false);
|
||||
}
|
||||
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
while(IsOperatorControl() && IsEnabled()) {
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Send image to dashboard if IMAQ has not thrown an error
|
||||
void SendToDashboard(Image *image, int error)
|
||||
{
|
||||
if(error < ERR_SUCCESS) {
|
||||
DriverStation::ReportError("Send To Dashboard error: " + std::to_string((long)imaqError) + "\n");
|
||||
} else {
|
||||
CameraServer::GetInstance()->SetImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
//Comparator function for sorting particles. Returns true if particle 1 is larger
|
||||
static bool CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
|
||||
{
|
||||
//we want descending sort order
|
||||
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
|
||||
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
|
||||
*/
|
||||
double ratioToScore(double ratio)
|
||||
{
|
||||
return (fmax(0, fmin(100*(1-fabs(1-ratio)), 100)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score convex hull area. This scores how "complete" the particle is. Particles with large holes will score worse than a filled in shape
|
||||
*/
|
||||
double ConvexHullAreaScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore((report.Area/report.ConvexHullArea)*1.18);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the particle appears to be a trapezoid. Compares the convex hull (filled in) area to the area of the bounding box.
|
||||
* The expectation is that the convex hull area is about 95.4% of the bounding box area for an ideal tote.
|
||||
*/
|
||||
double TrapezoidScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(report.ConvexHullArea/((report.BoundingRectRight-report.BoundingRectLeft)*(report.BoundingRectBottom-report.BoundingRectTop)*.954));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the long side of a tote.
|
||||
*/
|
||||
double LongSideScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/LONG_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the short side of a tote.
|
||||
*/
|
||||
double ShortSideScore(ParticleReport report){
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/SHORT_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
|
||||
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
|
||||
*
|
||||
* @param image The image to use for measuring the particle estimated rectangle
|
||||
* @param report The Particle Analysis Report for the particle
|
||||
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
|
||||
* @return The estimated distance to the target in feet.
|
||||
*/
|
||||
double computeDistance (Image *image, ParticleReport report, bool isLong) {
|
||||
double normalizedWidth, targetWidth;
|
||||
int xRes, yRes;
|
||||
|
||||
imaqGetImageSize(image, &xRes, &yRes);
|
||||
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/xRes;
|
||||
SmartDashboard::PutNumber("Width", normalizedWidth);
|
||||
targetWidth = isLong ? 26.9 : 16.9;
|
||||
|
||||
return targetWidth/(normalizedWidth*12*tan(VIEW_ANGLE*M_PI/(180*2)));
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(VisionColor2015Sample);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "WPILib.h"
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
* Example of finding yellow totes based on retroreflective target.
|
||||
* This example utilizes an image file, which you need to copy to the roboRIO
|
||||
* To use a camera you will have to integrate the appropriate camera details with this example.
|
||||
* To use a USB camera instead, see the IntermediateVision example for details
|
||||
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
|
||||
* using an Axis Camera.
|
||||
*
|
||||
* Sample images can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
|
||||
*/
|
||||
class VisionRetro2015Sample : public SampleRobot
|
||||
{
|
||||
//A structure to hold measurements of a particle
|
||||
struct ParticleReport {
|
||||
double PercentAreaToImageArea;
|
||||
double Area;
|
||||
double BoundingRectLeft;
|
||||
double BoundingRectTop;
|
||||
double BoundingRectRight;
|
||||
double BoundingRectBottom;
|
||||
};
|
||||
|
||||
//Structure to represent the scores for the various tests used for target identification
|
||||
struct Scores {
|
||||
double Area;
|
||||
double Aspect;
|
||||
};
|
||||
|
||||
//Images
|
||||
Image *frame;
|
||||
Image *binaryFrame;
|
||||
int imaqError;
|
||||
|
||||
//Constants
|
||||
Range RING_HUE_RANGE = {101, 64}; //Default hue range for ring light
|
||||
Range RING_SAT_RANGE = {88, 255}; //Default saturation range for ring light
|
||||
Range RING_VAL_RANGE = {134, 255}; //Default value range for ring light
|
||||
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
|
||||
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
|
||||
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
|
||||
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
|
||||
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
|
||||
ParticleFilterCriteria2 criteria[1];
|
||||
ParticleFilterOptions2 filterOptions = {0,0,1,1};
|
||||
Scores scores;
|
||||
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
// create images
|
||||
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
||||
binaryFrame = imaqCreateImage(IMAQ_IMAGE_U8, 0);
|
||||
|
||||
//Put default values to SmartDashboard so fields will appear
|
||||
SmartDashboard::PutNumber("Tote hue min", RING_HUE_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote hue max", RING_HUE_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Tote sat min", RING_SAT_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote sat max", RING_SAT_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Tote val min", RING_VAL_RANGE.minValue);
|
||||
SmartDashboard::PutNumber("Tote val max", RING_VAL_RANGE.maxValue);
|
||||
SmartDashboard::PutNumber("Area min %", AREA_MINIMUM);
|
||||
}
|
||||
|
||||
void Autonomous() override {
|
||||
while (IsAutonomous() && IsEnabled())
|
||||
{
|
||||
//read file in from disk. For this example to run you need to copy image.jpg from the SampleImages folder to the
|
||||
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
|
||||
imaqError = imaqReadFile(frame, "//home//lvuser//SampleImages//image.jpg", NULL, NULL);
|
||||
|
||||
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
|
||||
RING_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", RING_HUE_RANGE.minValue);
|
||||
RING_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", RING_HUE_RANGE.maxValue);
|
||||
RING_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", RING_SAT_RANGE.minValue);
|
||||
RING_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", RING_SAT_RANGE.maxValue);
|
||||
RING_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", RING_VAL_RANGE.minValue);
|
||||
RING_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", RING_VAL_RANGE.maxValue);
|
||||
|
||||
//Threshold the image looking for ring light color
|
||||
imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &RING_HUE_RANGE, &RING_SAT_RANGE, &RING_VAL_RANGE);
|
||||
|
||||
//Send particle count to dashboard
|
||||
int numParticles = 0;
|
||||
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
|
||||
SmartDashboard::PutNumber("Masked particles", numParticles);
|
||||
|
||||
//Send masked image to dashboard to assist in tweaking mask.
|
||||
SendToDashboard(binaryFrame, imaqError);
|
||||
|
||||
//filter out small particles
|
||||
float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
|
||||
criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
|
||||
imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);
|
||||
|
||||
//Send particle count after filtering to dashboard
|
||||
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
|
||||
SmartDashboard::PutNumber("Filtered particles", numParticles);
|
||||
|
||||
if(numParticles > 0) {
|
||||
//Measure particles and sort by particle size
|
||||
std::vector<ParticleReport> particles;
|
||||
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
|
||||
{
|
||||
ParticleReport par;
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
|
||||
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
|
||||
particles.push_back(par);
|
||||
}
|
||||
sort(particles.begin(), particles.end(), CompareParticleSizes);
|
||||
|
||||
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
|
||||
//for the reader. Note that this scores and reports information about a single particle (single L shaped target). To get accurate information
|
||||
//about the location of the tote (not just the distance) you will need to correlate two adjacent targets in order to find the true center of the tote.
|
||||
scores.Aspect = AspectScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Aspect", scores.Aspect);
|
||||
scores.Area = AreaScore(particles.at(0));
|
||||
SmartDashboard::PutNumber("Area", scores.Area);
|
||||
bool isTarget = scores.Area > SCORE_MIN && scores.Aspect > SCORE_MIN;
|
||||
|
||||
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
|
||||
SmartDashboard::PutBoolean("IsTarget", isTarget);
|
||||
SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0)));
|
||||
} else {
|
||||
SmartDashboard::PutBoolean("IsTarget", false);
|
||||
}
|
||||
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
while(IsOperatorControl() && IsEnabled()) {
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Send image to dashboard if IMAQ has not thrown an error
|
||||
void SendToDashboard(Image *image, int error)
|
||||
{
|
||||
if(error < ERR_SUCCESS) {
|
||||
DriverStation::ReportError("Send To Dashboard error: " + std::to_string((long)imaqError) + "\n");
|
||||
} else {
|
||||
CameraServer::GetInstance()->SetImage(binaryFrame);
|
||||
}
|
||||
}
|
||||
|
||||
//Comparator function for sorting particles. Returns true if particle 1 is larger
|
||||
static bool CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
|
||||
{
|
||||
//we want descending sort order
|
||||
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
|
||||
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
|
||||
*/
|
||||
double ratioToScore(double ratio)
|
||||
{
|
||||
return (fmax(0, fmin(100*(1-fabs(1-ratio)), 100)));
|
||||
}
|
||||
|
||||
|
||||
double AreaScore(ParticleReport report)
|
||||
{
|
||||
double boundingArea = (report.BoundingRectBottom - report.BoundingRectTop) * (report.BoundingRectRight - report.BoundingRectLeft);
|
||||
//Tape is 7" edge so 49" bounding rect. With 2" wide tape it covers 24" of the rect.
|
||||
return ratioToScore((49/24)*report.Area/boundingArea);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the retro-reflective target. Target is 7"x7" so aspect should be 1
|
||||
*/
|
||||
double AspectScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
|
||||
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
|
||||
*
|
||||
* @param image The image to use for measuring the particle estimated rectangle
|
||||
* @param report The Particle Analysis Report for the particle
|
||||
* @return The estimated distance to the target in feet.
|
||||
*/
|
||||
double computeDistance (Image *image, ParticleReport report) {
|
||||
double normalizedWidth, targetWidth;
|
||||
int xRes, yRes;
|
||||
|
||||
imaqGetImageSize(image, &xRes, &yRes);
|
||||
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/xRes;
|
||||
SmartDashboard::PutNumber("Width", normalizedWidth);
|
||||
targetWidth = 7;
|
||||
|
||||
return targetWidth/(normalizedWidth*12*tan(VIEW_ANGLE*M_PI/(180*2)));
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(VisionRetro2015Sample);
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "WPILib.h"
|
||||
|
||||
/**
|
||||
* Uses AxisCamera class to manually acquire a new image each frame, and annotate the image by drawing
|
||||
* a circle on it, and show it on the FRC Dashboard.
|
||||
*/
|
||||
class AxisCameraSample : public SampleRobot
|
||||
{
|
||||
IMAQdxSession session;
|
||||
Image *frame;
|
||||
IMAQdxError imaqError;
|
||||
AxisCamera *camera;
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
// create an image
|
||||
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
||||
|
||||
// open the camera at the IP address assigned. This is the IP address that the camera
|
||||
// can be accessed through the web interface.
|
||||
camera = new AxisCamera("10.1.91.103");
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
// grab an image, draw the circle, and provide it for the camera server which will
|
||||
// in turn send it to the dashboard.
|
||||
while(IsOperatorControl() && IsEnabled()) {
|
||||
camera->GetImage(frame);
|
||||
imaqDrawShapeOnImage(frame, frame, { 10, 10, 100, 100 }, DrawMode::IMAQ_DRAW_VALUE, ShapeMode::IMAQ_SHAPE_OVAL, 0.0f);
|
||||
CameraServer::GetInstance()->SetImage(frame);
|
||||
Wait(0.05);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(AxisCameraSample);
|
||||
|
||||
@@ -8,14 +8,21 @@ class IntermediateVisionRobot : public SampleRobot
|
||||
{
|
||||
IMAQdxSession session;
|
||||
Image *frame;
|
||||
IMAQdxError imaqError;
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
// create an image
|
||||
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
||||
// open the camera
|
||||
IMAQdxOpenCamera("cam1", IMAQdxCameraControlModeController, &session);
|
||||
IMAQdxConfigureGrab(session);
|
||||
//the camera name (ex "cam0") can be found through the roborio web interface
|
||||
imaqError = IMAQdxOpenCamera("cam0", IMAQdxCameraControlModeController, &session);
|
||||
if(imaqError != IMAQdxErrorSuccess) {
|
||||
DriverStation::ReportError("IMAQdxOpenCamera error: " + std::to_string((long)imaqError) + "\n");
|
||||
}
|
||||
imaqError = IMAQdxConfigureGrab(session);
|
||||
if(imaqError != IMAQdxErrorSuccess) {
|
||||
DriverStation::ReportError("IMAQdxConfigureGrab error: " + std::to_string((long)imaqError) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
@@ -26,10 +33,13 @@ public:
|
||||
// in turn send it to the dashboard.
|
||||
while(IsOperatorControl() && IsEnabled()) {
|
||||
IMAQdxGrab(session, frame, true, NULL);
|
||||
|
||||
imaqDrawShapeOnImage(frame, frame, { 10, 10, 100, 100 }, DrawMode::IMAQ_DRAW_VALUE, ShapeMode::IMAQ_SHAPE_OVAL, 0.0f);
|
||||
|
||||
CameraServer::GetInstance()->SetImage(frame);
|
||||
if(imaqError != IMAQdxErrorSuccess) {
|
||||
DriverStation::ReportError("IMAQdxGrab error: " + std::to_string((long)imaqError) + "\n");
|
||||
} else {
|
||||
imaqDrawShapeOnImage(frame, frame, { 10, 10, 100, 100 }, DrawMode::IMAQ_DRAW_VALUE, ShapeMode::IMAQ_SHAPE_OVAL, 0.0f);
|
||||
CameraServer::GetInstance()->SetImage(frame);
|
||||
}
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
// stop image acquisition
|
||||
IMAQdxStopAcquisition(session);
|
||||
|
||||
@@ -10,11 +10,18 @@ class QuickVisionRobot : public SampleRobot
|
||||
{
|
||||
public:
|
||||
void RobotInit() override {
|
||||
CameraServer::GetInstance()->SetQuality(75);
|
||||
CameraServer::GetInstance()->StartAutomaticCapture();
|
||||
CameraServer::GetInstance()->SetQuality(50);
|
||||
//the camera name (ex "cam0") can be found through the roborio web interface
|
||||
CameraServer::GetInstance()->StartAutomaticCapture("cam0");
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
void OperatorControl()
|
||||
{
|
||||
while (IsOperatorControl() && IsEnabled())
|
||||
{
|
||||
/** robot code here! **/
|
||||
Wait(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Simple vision program</name>
|
||||
<name>Simple Vision</name>
|
||||
<description>The minimal program to acquire images from an attached USB camera on the robot
|
||||
and send them to the dashboard.</description>
|
||||
<tags>
|
||||
@@ -298,7 +298,7 @@
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Intermediate vision</name>
|
||||
<name>Intermediate Vision</name>
|
||||
<description>An example program that acquires images from an attached USB camera and adds some
|
||||
annotation to the image as you might do for showing operators the result of some image
|
||||
recognition, and sends it to the dashboard for display.
|
||||
@@ -316,6 +316,65 @@
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Axis Camera Sample</name>
|
||||
<description>An example program that acquires images from an Axis network camera and adds some
|
||||
annotation to the image as you might do for showing operators the result of some image
|
||||
recognition, and sends it to the dashboard for display. This demonstrates the use of the
|
||||
AxisCamera class.
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/AxisCameraSample/src/Robot.cpp"
|
||||
destination="src/Robot.cpp"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>2015 Vision Color Sample</name>
|
||||
<description>An example program that demonstrates image processing to locate Yellow totes by color.
|
||||
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
|
||||
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
|
||||
camera example;
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/2015Vision/Color_src/Robot.cpp"
|
||||
destination="src/Robot.cpp"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>2015 Vision Retro Sample</name>
|
||||
<description>An example program that demonstrates image processing to locate Yellow totes by the retroreflective target.
|
||||
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
|
||||
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
|
||||
camera example;
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/2015Vision/Retro_src/Robot.cpp"
|
||||
destination="src/Robot.cpp"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>GearsBot</name>
|
||||
|
||||
@@ -8,7 +8,7 @@ command.dir=/home/lvuser/
|
||||
# Libraries to use
|
||||
wpilib=${user.home}/wpilib/cpp/${cpp-version}
|
||||
wpilib.lib=${wpilib}/lib
|
||||
roboRIOAllowedImages=20
|
||||
roboRIOAllowedImages=23
|
||||
|
||||
# Ant support
|
||||
wpilib.ant.dir=${wpilib}/ant
|
||||
|
||||
@@ -64,6 +64,15 @@
|
||||
|
||||
<echo>[athena-deploy] Copying code over.</echo>
|
||||
<scp file="${out.exe}" sftp="true" todir="${username}@${target}:${deploy.dir}" password="${password}" trust="true"/>
|
||||
|
||||
<sshexec host="${target}"
|
||||
username="admin"
|
||||
password="${password}"
|
||||
trust="true"
|
||||
failonerror="false"
|
||||
command="killall netconsole-host"/>
|
||||
<scp file="${wpilib.ant.dir}/netconsole-host" todir="admin@${target}:/usr/local/frc/bin" password="${password}" trust="true"/>
|
||||
|
||||
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:/home/lvuser/" password="${password}" trust="true"/>
|
||||
|
||||
<echo>[athena-deploy] Starting program.</echo>
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,240 @@
|
||||
package $package;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.util.Comparator;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.ni.vision.NIVision;
|
||||
import com.ni.vision.NIVision.Image;
|
||||
import com.ni.vision.NIVision.ImageType;
|
||||
|
||||
import edu.wpi.first.wpilibj.CameraServer;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
|
||||
/**
|
||||
* Example of finding yellow totes based on color.
|
||||
* This example utilizes an image file, which you need to copy to the roboRIO
|
||||
* To use a camera you will have to integrate the appropriate camera details with this example.
|
||||
* To use a USB camera instead, see the SimpelVision and AdvancedVision examples for details
|
||||
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
|
||||
* using an Axis Camera.
|
||||
*
|
||||
* Sample omages can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
//A structure to hold measurements of a particle
|
||||
public class ParticleReport implements Comparator<ParticleReport>, Comparable<ParticleReport>{
|
||||
double PercentAreaToImageArea;
|
||||
double Area;
|
||||
double ConvexHullArea;
|
||||
double BoundingRectLeft;
|
||||
double BoundingRectTop;
|
||||
double BoundingRectRight;
|
||||
double BoundingRectBottom;
|
||||
|
||||
public int compareTo(ParticleReport r)
|
||||
{
|
||||
return (int)(r.Area - this.Area);
|
||||
}
|
||||
|
||||
public int compare(ParticleReport r1, ParticleReport r2)
|
||||
{
|
||||
return (int)(r1.Area - r2.Area);
|
||||
}
|
||||
};
|
||||
|
||||
//Structure to represent the scores for the various tests used for target identification
|
||||
public class Scores {
|
||||
double Trapezoid;
|
||||
double LongAspect;
|
||||
double ShortAspect;
|
||||
double AreaToConvexHullArea;
|
||||
};
|
||||
|
||||
//Images
|
||||
Image frame;
|
||||
Image binaryFrame;
|
||||
int imaqError;
|
||||
|
||||
//Constants
|
||||
NIVision.Range TOTE_HUE_RANGE = new NIVision.Range(24, 49); //Default hue range for yellow tote
|
||||
NIVision.Range TOTE_SAT_RANGE = new NIVision.Range(67, 255); //Default saturation range for yellow tote
|
||||
NIVision.Range TOTE_VAL_RANGE = new NIVision.Range(49, 255); //Default value range for yellow tote
|
||||
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
|
||||
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
|
||||
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
|
||||
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
|
||||
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
|
||||
NIVision.ParticleFilterCriteria2 criteria[] = new NIVision.ParticleFilterCriteria2[1];
|
||||
NIVision.ParticleFilterOptions2 filterOptions = new NIVision.ParticleFilterOptions2(0,0,1,1);
|
||||
Scores scores = new Scores();
|
||||
|
||||
public void robotInit() {
|
||||
// create images
|
||||
frame = NIVision.imaqCreateImage(ImageType.IMAGE_RGB, 0);
|
||||
binaryFrame = NIVision.imaqCreateImage(ImageType.IMAGE_U8, 0);
|
||||
criteria[0] = new NIVision.ParticleFilterCriteria2(NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA, AREA_MINIMUM, 100.0, 0, 0);
|
||||
|
||||
//Put default values to SmartDashboard so fields will appear
|
||||
SmartDashboard.putNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Area min %", AREA_MINIMUM);
|
||||
}
|
||||
|
||||
public void autonomous() {
|
||||
while (isAutonomous() && isEnabled())
|
||||
{
|
||||
//read file in from disk. For this example to run you need to copy image20.jpg from the SampleImages folder to the
|
||||
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
|
||||
NIVision.imaqReadFile(frame, "/home/lvuser/SampleImages/image20.jpg");
|
||||
|
||||
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
|
||||
TOTE_HUE_RANGE.minValue = (int)SmartDashboard.getNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
TOTE_HUE_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
TOTE_SAT_RANGE.minValue = (int)SmartDashboard.getNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
TOTE_SAT_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
TOTE_VAL_RANGE.minValue = (int)SmartDashboard.getNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
TOTE_VAL_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
|
||||
//Threshold the image looking for yellow (tote color)
|
||||
NIVision.imaqColorThreshold(binaryFrame, frame, 255, NIVision.ColorMode.HSV, TOTE_HUE_RANGE, TOTE_SAT_RANGE, TOTE_VAL_RANGE);
|
||||
|
||||
//Send particle count to dashboard
|
||||
int numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
|
||||
SmartDashboard.putNumber("Masked particles", numParticles);
|
||||
|
||||
//Send masked image to dashboard to assist in tweaking mask.
|
||||
CameraServer.getInstance().setImage(binaryFrame);
|
||||
|
||||
//filter out small particles
|
||||
float areaMin = (float)SmartDashboard.getNumber("Area min %", AREA_MINIMUM);
|
||||
criteria[0].lower = areaMin;
|
||||
imaqError = NIVision.imaqParticleFilter4(binaryFrame, binaryFrame, criteria, filterOptions, null);
|
||||
|
||||
//Send particle count after filtering to dashboard
|
||||
numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
|
||||
SmartDashboard.putNumber("Filtered particles", numParticles);
|
||||
|
||||
if(numParticles > 0)
|
||||
{
|
||||
//Measure particles and sort by particle size
|
||||
Vector<ParticleReport> particles = new Vector<ParticleReport>();
|
||||
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
|
||||
{
|
||||
ParticleReport par = new ParticleReport();
|
||||
par.PercentAreaToImageArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA);
|
||||
par.Area = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA);
|
||||
par.ConvexHullArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_CONVEX_HULL_AREA);
|
||||
par.BoundingRectTop = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_TOP);
|
||||
par.BoundingRectLeft = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_LEFT);
|
||||
par.BoundingRectBottom = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_BOTTOM);
|
||||
par.BoundingRectRight = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_RIGHT);
|
||||
particles.add(par);
|
||||
}
|
||||
particles.sort(null);
|
||||
|
||||
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
|
||||
//for the reader. Note that the long and short side scores expect a single tote and will not work for a stack of 2 or more totes.
|
||||
//Modification of the code to accommodate 2 or more stacked totes is left as an exercise for the reader.
|
||||
scores.Trapezoid = TrapezoidScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Trapezoid", scores.Trapezoid);
|
||||
scores.LongAspect = LongSideScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Long Aspect", scores.LongAspect);
|
||||
scores.ShortAspect = ShortSideScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Short Aspect", scores.ShortAspect);
|
||||
scores.AreaToConvexHullArea = ConvexHullAreaScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Convex Hull Area", scores.AreaToConvexHullArea);
|
||||
boolean isTote = scores.Trapezoid > SCORE_MIN && (scores.LongAspect > SCORE_MIN || scores.ShortAspect > SCORE_MIN) && scores.AreaToConvexHullArea > SCORE_MIN;
|
||||
boolean isLong = scores.LongAspect > scores.ShortAspect;
|
||||
|
||||
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
|
||||
SmartDashboard.putBoolean("IsTote", isTote);
|
||||
SmartDashboard.putNumber("Distance", computeDistance(binaryFrame, particles.elementAt(0), isLong));
|
||||
} else {
|
||||
SmartDashboard.putBoolean("IsTote", false);
|
||||
}
|
||||
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
while(isOperatorControl() && isEnabled()) {
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
//Comparator function for sorting particles. Returns true if particle 1 is larger
|
||||
static boolean CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
|
||||
{
|
||||
//we want descending sort order
|
||||
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
|
||||
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
|
||||
*/
|
||||
double ratioToScore(double ratio)
|
||||
{
|
||||
return (Math.max(0, Math.min(100*(1-Math.abs(1-ratio)), 100)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score convex hull area. This scores how "complete" the particle is. Particles with large holes will score worse than a filled in shape
|
||||
*/
|
||||
double ConvexHullAreaScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore((report.Area/report.ConvexHullArea)*1.18);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the particle appears to be a trapezoid. Compares the convex hull (filled in) area to the area of the bounding box.
|
||||
* The expectation is that the convex hull area is about 95.4% of the bounding box area for an ideal tote.
|
||||
*/
|
||||
double TrapezoidScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(report.ConvexHullArea/((report.BoundingRectRight-report.BoundingRectLeft)*(report.BoundingRectBottom-report.BoundingRectTop)*.954));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the long side of a tote.
|
||||
*/
|
||||
double LongSideScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/LONG_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the short side of a tote.
|
||||
*/
|
||||
double ShortSideScore(ParticleReport report){
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/SHORT_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
|
||||
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
|
||||
*
|
||||
* @param image The image to use for measuring the particle estimated rectangle
|
||||
* @param report The Particle Analysis Report for the particle
|
||||
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
|
||||
* @return The estimated distance to the target in feet.
|
||||
*/
|
||||
double computeDistance (Image image, ParticleReport report, boolean isLong) {
|
||||
double normalizedWidth, targetWidth;
|
||||
NIVision.GetImageSizeResult size;
|
||||
|
||||
size = NIVision.imaqGetImageSize(image);
|
||||
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/size.width;
|
||||
targetWidth = isLong ? 26.0 : 16.9;
|
||||
|
||||
return targetWidth/(normalizedWidth*12*Math.tan(VIEW_ANGLE*Math.PI/(180*2)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package $package;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.util.Comparator;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.ni.vision.NIVision;
|
||||
import com.ni.vision.NIVision.Image;
|
||||
import com.ni.vision.NIVision.ImageType;
|
||||
|
||||
import edu.wpi.first.wpilibj.CameraServer;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||
|
||||
/**
|
||||
* Example of finding yellow totes based on retroreflective target.
|
||||
* This example utilizes an image file, which you need to copy to the roboRIO
|
||||
* To use a camera you will have to integrate the appropriate camera details with this example.
|
||||
* To use a USB camera instead, see the SimpelVision and AdvancedVision examples for details
|
||||
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
|
||||
* using an Axis Camera.
|
||||
*
|
||||
* Sample images can found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
//A structure to hold measurements of a particle
|
||||
public class ParticleReport implements Comparator<ParticleReport>, Comparable<ParticleReport>{
|
||||
double PercentAreaToImageArea;
|
||||
double Area;
|
||||
double BoundingRectLeft;
|
||||
double BoundingRectTop;
|
||||
double BoundingRectRight;
|
||||
double BoundingRectBottom;
|
||||
|
||||
public int compareTo(ParticleReport r)
|
||||
{
|
||||
return (int)(r.Area - this.Area);
|
||||
}
|
||||
|
||||
public int compare(ParticleReport r1, ParticleReport r2)
|
||||
{
|
||||
return (int)(r1.Area - r2.Area);
|
||||
}
|
||||
};
|
||||
|
||||
//Structure to represent the scores for the various tests used for target identification
|
||||
public class Scores {
|
||||
double Area;
|
||||
double Aspect;
|
||||
};
|
||||
|
||||
//Images
|
||||
Image frame;
|
||||
Image binaryFrame;
|
||||
int imaqError;
|
||||
|
||||
//Constants
|
||||
NIVision.Range TOTE_HUE_RANGE = new NIVision.Range(101, 64); //Default hue range for yellow tote
|
||||
NIVision.Range TOTE_SAT_RANGE = new NIVision.Range(88, 255); //Default saturation range for yellow tote
|
||||
NIVision.Range TOTE_VAL_RANGE = new NIVision.Range(134, 255); //Default value range for yellow tote
|
||||
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
|
||||
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
|
||||
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
|
||||
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
|
||||
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
|
||||
NIVision.ParticleFilterCriteria2 criteria[] = new NIVision.ParticleFilterCriteria2[1];
|
||||
NIVision.ParticleFilterOptions2 filterOptions = new NIVision.ParticleFilterOptions2(0,0,1,1);
|
||||
Scores scores = new Scores();
|
||||
|
||||
public void robotInit() {
|
||||
// create images
|
||||
frame = NIVision.imaqCreateImage(ImageType.IMAGE_RGB, 0);
|
||||
binaryFrame = NIVision.imaqCreateImage(ImageType.IMAGE_U8, 0);
|
||||
criteria[0] = new NIVision.ParticleFilterCriteria2(NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA, AREA_MINIMUM, 100.0, 0, 0);
|
||||
|
||||
//Put default values to SmartDashboard so fields will appear
|
||||
SmartDashboard.putNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
SmartDashboard.putNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
SmartDashboard.putNumber("Area min %", AREA_MINIMUM);
|
||||
}
|
||||
|
||||
public void autonomous() {
|
||||
while (isAutonomous() && isEnabled())
|
||||
{
|
||||
//read file in from disk. For this example to run you need to copy image.jpg from the SampleImages folder to the
|
||||
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
|
||||
NIVision.imaqReadFile(frame, "/home/lvuser/SampleImages/image.jpg");
|
||||
|
||||
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
|
||||
TOTE_HUE_RANGE.minValue = (int)SmartDashboard.getNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
|
||||
TOTE_HUE_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
|
||||
TOTE_SAT_RANGE.minValue = (int)SmartDashboard.getNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
|
||||
TOTE_SAT_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
|
||||
TOTE_VAL_RANGE.minValue = (int)SmartDashboard.getNumber("Tote val min", TOTE_VAL_RANGE.minValue);
|
||||
TOTE_VAL_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
|
||||
|
||||
//Threshold the image looking for yellow (tote color)
|
||||
NIVision.imaqColorThreshold(binaryFrame, frame, 255, NIVision.ColorMode.HSV, TOTE_HUE_RANGE, TOTE_SAT_RANGE, TOTE_VAL_RANGE);
|
||||
|
||||
//Send particle count to dashboard
|
||||
int numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
|
||||
SmartDashboard.putNumber("Masked particles", numParticles);
|
||||
|
||||
//Send masked image to dashboard to assist in tweaking mask.
|
||||
CameraServer.getInstance().setImage(binaryFrame);
|
||||
|
||||
//filter out small particles
|
||||
float areaMin = (float)SmartDashboard.getNumber("Area min %", AREA_MINIMUM);
|
||||
criteria[0].lower = areaMin;
|
||||
imaqError = NIVision.imaqParticleFilter4(binaryFrame, binaryFrame, criteria, filterOptions, null);
|
||||
|
||||
//Send particle count after filtering to dashboard
|
||||
numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
|
||||
SmartDashboard.putNumber("Filtered particles", numParticles);
|
||||
|
||||
if(numParticles > 0)
|
||||
{
|
||||
//Measure particles and sort by particle size
|
||||
Vector<ParticleReport> particles = new Vector<ParticleReport>();
|
||||
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
|
||||
{
|
||||
ParticleReport par = new ParticleReport();
|
||||
par.PercentAreaToImageArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA);
|
||||
par.Area = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA);
|
||||
par.BoundingRectTop = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_TOP);
|
||||
par.BoundingRectLeft = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_LEFT);
|
||||
par.BoundingRectBottom = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_BOTTOM);
|
||||
par.BoundingRectRight = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_RIGHT);
|
||||
particles.add(par);
|
||||
}
|
||||
particles.sort(null);
|
||||
|
||||
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
|
||||
//for the reader. Note that this scores and reports information about a single particle (single L shaped target). To get accurate information
|
||||
//about the location of the tote (not just the distance) you will need to correlate two adjacent targets in order to find the true center of the tote.
|
||||
scores.Aspect = AspectScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Aspect", scores.Aspect);
|
||||
scores.Area = AreaScore(particles.elementAt(0));
|
||||
SmartDashboard.putNumber("Area", scores.Area);
|
||||
boolean isTote = scores.Aspect > SCORE_MIN && scores.Area > SCORE_MIN;
|
||||
|
||||
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
|
||||
SmartDashboard.putBoolean("IsTote", isTote);
|
||||
SmartDashboard.putNumber("Distance", computeDistance(binaryFrame, particles.elementAt(0)));
|
||||
} else {
|
||||
SmartDashboard.putBoolean("IsTote", false);
|
||||
}
|
||||
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
while(isOperatorControl() && isEnabled()) {
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
//Comparator function for sorting particles. Returns true if particle 1 is larger
|
||||
static boolean CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
|
||||
{
|
||||
//we want descending sort order
|
||||
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
|
||||
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
|
||||
*/
|
||||
double ratioToScore(double ratio)
|
||||
{
|
||||
return (Math.max(0, Math.min(100*(1-Math.abs(1-ratio)), 100)));
|
||||
}
|
||||
|
||||
double AreaScore(ParticleReport report)
|
||||
{
|
||||
double boundingArea = (report.BoundingRectBottom - report.BoundingRectTop) * (report.BoundingRectRight - report.BoundingRectLeft);
|
||||
//Tape is 7" edge so 49" bounding rect. With 2" wide tape it covers 24" of the rect.
|
||||
return ratioToScore((49/24)*report.Area/boundingArea);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to score if the aspect ratio of the particle appears to match the retro-reflective target. Target is 7"x7" so aspect should be 1
|
||||
*/
|
||||
double AspectScore(ParticleReport report)
|
||||
{
|
||||
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
|
||||
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
|
||||
*
|
||||
* @param image The image to use for measuring the particle estimated rectangle
|
||||
* @param report The Particle Analysis Report for the particle
|
||||
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
|
||||
* @return The estimated distance to the target in feet.
|
||||
*/
|
||||
double computeDistance (Image image, ParticleReport report) {
|
||||
double normalizedWidth, targetWidth;
|
||||
NIVision.GetImageSizeResult size;
|
||||
|
||||
size = NIVision.imaqGetImageSize(image);
|
||||
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/size.width;
|
||||
targetWidth = 7;
|
||||
|
||||
return targetWidth/(normalizedWidth*12*Math.tan(VIEW_ANGLE*Math.PI/(180*2)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package $package;
|
||||
|
||||
import com.ni.vision.NIVision;
|
||||
import com.ni.vision.NIVision.DrawMode;
|
||||
import com.ni.vision.NIVision.Image;
|
||||
import com.ni.vision.NIVision.ShapeMode;
|
||||
|
||||
import edu.wpi.first.wpilibj.CameraServer;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import edu.wpi.first.wpilibj.vision.AxisCamera;
|
||||
|
||||
/**
|
||||
* This demo shows the use of the AxisCamera class.
|
||||
* Uses AxisCamera class to manually acquire a new image each frame, and annotate the image by drawing
|
||||
* a circle on it, and show it on the FRC Dashboard.
|
||||
*/
|
||||
|
||||
public class Robot extends SampleRobot {
|
||||
int session;
|
||||
Image frame;
|
||||
AxisCamera camera;
|
||||
|
||||
public void robotInit() {
|
||||
|
||||
frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0);
|
||||
|
||||
// open the camera at the IP address assigned. This is the IP address that the camera
|
||||
// can be accessed through the web interface.
|
||||
camera = new AxisCamera("10.1.91.100");
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
|
||||
/**
|
||||
* grab an image from the camera, draw the circle, and provide it for the camera server
|
||||
* which will in turn send it to the dashboard.
|
||||
*/
|
||||
NIVision.Rect rect = new NIVision.Rect(10, 10, 100, 100);
|
||||
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
camera.getImage(frame);
|
||||
NIVision.imaqDrawShapeOnImage(frame, frame, rect,
|
||||
DrawMode.DRAW_VALUE, ShapeMode.SHAPE_OVAL, 0.0f);
|
||||
|
||||
CameraServer.getInstance().setImage(frame);
|
||||
|
||||
/** robot code here! **/
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package $package;
|
||||
|
||||
import com.ni.vision.NIVision;
|
||||
import com.ni.vision.NIVision.DrawMode;
|
||||
import com.ni.vision.NIVision.Image;
|
||||
import com.ni.vision.NIVision.ShapeMode;
|
||||
|
||||
import edu.wpi.first.wpilibj.CameraServer;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
|
||||
/**
|
||||
* This is a demo program showing the use of the NIVision class to do vision processing.
|
||||
* The image is acquired from the USB Webcam, then a circle is overlayed on it.
|
||||
* The NIVision class supplies dozens of methods for different types of processing.
|
||||
* The resulting image can then be sent to the FRC PC Dashboard with setImage()
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
int session;
|
||||
Image frame;
|
||||
|
||||
public void robotInit() {
|
||||
|
||||
frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0);
|
||||
|
||||
// the camera name (ex "cam0") can be found through the roborio web interface
|
||||
session = NIVision.IMAQdxOpenCamera("cam0",
|
||||
NIVision.IMAQdxCameraControlMode.CameraControlModeController);
|
||||
NIVision.IMAQdxConfigureGrab(session);
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
NIVision.IMAQdxStartAcquisition(session);
|
||||
|
||||
/**
|
||||
* grab an image, draw the circle, and provide it for the camera server
|
||||
* which will in turn send it to the dashboard.
|
||||
*/
|
||||
NIVision.Rect rect = new NIVision.Rect(10, 10, 100, 100);
|
||||
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
|
||||
NIVision.IMAQdxGrab(session, frame, 1);
|
||||
NIVision.imaqDrawShapeOnImage(frame, frame, rect,
|
||||
DrawMode.DRAW_VALUE, ShapeMode.SHAPE_OVAL, 0.0f);
|
||||
|
||||
CameraServer.getInstance().setImage(frame);
|
||||
|
||||
/** robot code here! **/
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
NIVision.IMAQdxStopAcquisition(session);
|
||||
}
|
||||
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,10 @@ public class Robot extends SampleRobot {
|
||||
final int joystickChannel = 0;
|
||||
|
||||
public Robot() {
|
||||
robotDrive.setExpiration(0.1);
|
||||
robotDrive = new RobotDrive(frontLeftChannel, rearLeftChannel, frontRightChannel, rearRightChannel);
|
||||
robotDrive.setInvertedMotor(MotorType.kFrontLeft, true); // invert the left side motors
|
||||
robotDrive.setInvertedMotor(MotorType.kRearLeft, true); // you may need to change or remove this to match your robot
|
||||
robotDrive.setExpiration(0.1);
|
||||
|
||||
stick = new Joystick(joystickChannel);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package $package;
|
||||
|
||||
import edu.wpi.first.wpilibj.CameraServer;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
|
||||
/**
|
||||
* This is a demo program showing the use of the CameraServer class.
|
||||
* With start automatic capture, there is no opportunity to process the image.
|
||||
* Look at the IntermediateVision sample for how to process the image before sending it to the FRC PC Dashboard.
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
|
||||
CameraServer server;
|
||||
|
||||
public Robot() {
|
||||
server = CameraServer.getInstance();
|
||||
server.setQuality(50);
|
||||
//the camera name (ex "cam0") can be found through the roborio web interface
|
||||
server.startAutomaticCapture("cam0");
|
||||
}
|
||||
|
||||
/**
|
||||
* start up automatic capture you should see the video stream from the
|
||||
* webcam in your FRC PC Dashboard.
|
||||
*/
|
||||
public void operatorControl() {
|
||||
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
/** robot code here! **/
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -81,12 +81,18 @@
|
||||
<description>Example programs that demonstrate the use of the various commonly used sensors on FRC robots</description>
|
||||
</tagDescription>
|
||||
|
||||
<tagDescription>
|
||||
<name>Vision</name>
|
||||
<description>Example programs that demonstrate the use of USB Cameras and image processing</description>
|
||||
</tagDescription>
|
||||
|
||||
<example>
|
||||
<name>Getting Started</name>
|
||||
<description>An example program which demonstrates the simplest autonomous and
|
||||
teleoperated routines.</description>
|
||||
<tags>
|
||||
<tag>Getting Started with Java</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
@@ -287,4 +293,97 @@
|
||||
destination="src/$package-dir/triggers/DoubleButton.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Simple Vision</name>
|
||||
<description>Demonstrate the use of the CameraServer class to stream from a USB Webcam without processing the images.</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/QuickVision/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Intermediate Vision</name>
|
||||
<description>Demonstrate the use of the NIVision class to capture image from a Webcam, process them, and then send them to the dashboard.</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/IntermediateVision/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>2015 Vision Color Sample</name>
|
||||
<description>An example program that demonstrates image processing to locate Yellow totes by color.
|
||||
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
|
||||
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
|
||||
camera example;
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/2015Vision/Color_src/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>2015 Vision Retro Sample</name>
|
||||
<description>An example program that demonstrates image processing to locate Yellow totes by the retroreflective target.
|
||||
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
|
||||
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
|
||||
camera example;
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/2015Vision/Retro_src/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Axis Camera Sample</name>
|
||||
<description>An example program that acquires images from an Axis network camera and adds some
|
||||
annotation to the image as you might do for showing operators the result of some image
|
||||
recognition, and sends it to the dashboard for display. This demonstrates the use of the
|
||||
AxisCamera class.
|
||||
</description>
|
||||
<tags>
|
||||
<tag>Vision</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/AxisCameraSample/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
</examples>
|
||||
|
||||
@@ -20,7 +20,7 @@ networktables.sources=${wpilib.lib}/NetworkTables-sources.jar
|
||||
#jnaerator.jar=${wpilib.lib}/jnaerator-runtime.jar
|
||||
#classpath=${wpilib.jar}:${networktables.jar}:${jna.jar}:${jnaerator.jar}
|
||||
classpath=${wpilib.jar}:${networktables.jar}
|
||||
roboRIOAllowedImages=20
|
||||
roboRIOAllowedImages=23
|
||||
|
||||
# Ant support
|
||||
wpilib.ant.dir=${wpilib}/ant
|
||||
|
||||
@@ -104,7 +104,16 @@
|
||||
<target name="deploy" depends="clean,jar,get-target-ip,dependencies" description="Deploy the jar and start the program running.">
|
||||
<echo>[athena-deploy] Copying code over.</echo>
|
||||
<scp file="${dist.jar}" todir="${username}@${target}:${deploy.dir}" password="${password}" trust="true"/>
|
||||
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:${command.dir}" password="${password}" trust="true"/>
|
||||
|
||||
<sshexec host="${target}"
|
||||
username="admin"
|
||||
password="${password}"
|
||||
trust="true"
|
||||
failonerror="false"
|
||||
command="killall netconsole-host"/>
|
||||
<scp file="${wpilib.ant.dir}/netconsole-host" todir="admin@${target}:/usr/local/frc/bin" password="${password}" trust="true"/>
|
||||
|
||||
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:${command.dir}" password="${password}" trust="true"/>
|
||||
|
||||
<echo>[athena-deploy] Starting program.</echo>
|
||||
<sshexec host="${target}"
|
||||
@@ -221,7 +230,7 @@
|
||||
</bool>
|
||||
</assert>
|
||||
<echo>roboRIO image version validated</echo>
|
||||
<echo>Checking for JRE. If this fails install the JRE using these instructions: http://wpilib.screenstepslive.com/s/4485/m/13809/l/243933-installing-java-8-on-the-roborio-java-only</echo>
|
||||
<echo>Checking for JRE. If this fails install the JRE using these instructions: https://wpilib.screenstepslive.com/s/4485/m/13503/l/288822-installing-java-8-on-the-roborio-using-the-frc-roborio-java-installer-java-only</echo>
|
||||
<sshexec host="${target}"
|
||||
username="${username}"
|
||||
password="${password}"
|
||||
|
||||
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
/usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -jar /home/lvuser/FRCUserProgram.jar
|
||||
env LD_PRELOAD=/lib/libstdc++.so.6.0.20 /usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -jar /home/lvuser/FRCUserProgram.jar
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -XX:+UsePerfData -agentlib:jdwp=transport=dt_socket,address=8348,server=y,suspend=y -jar /home/lvuser/FRCUserProgram.jar
|
||||
env LD_PRELOAD=/lib/libstdc++.so.6.0.20 /usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -XX:+UsePerfData -agentlib:jdwp=transport=dt_socket,address=8348,server=y,suspend=y -jar /home/lvuser/FRCUserProgram.jar
|
||||
|
||||
|
||||
@@ -23,6 +23,14 @@ extern "C" {
|
||||
|
||||
bool getPressureSwitch(void *pcm_pointer, int32_t *status);
|
||||
float getCompressorCurrent(void *pcm_pointer, int32_t *status);
|
||||
|
||||
bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status);
|
||||
bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status);
|
||||
bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status);
|
||||
bool getCompressorShortedFault(void *pcm_pointer, int32_t *status);
|
||||
bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status);
|
||||
bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status);
|
||||
void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,6 +86,8 @@ extern "C"
|
||||
void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage,
|
||||
int32_t *status);
|
||||
uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status);
|
||||
void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
|
||||
bool edgeSensitive, int32_t *status);
|
||||
|
||||
uint16_t getLoopTiming(int32_t *status);
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#define CTR_TxTimeout_MESSAGE "CTRE CAN Transmit Timeout"
|
||||
#define CTR_InvalidParamValue_MESSAGE "CTRE CAN Invalid Parameter"
|
||||
#define CTR_UnexpectedArbId_MESSAGE "CTRE Unexpected Arbitration ID (CAN Node ID)"
|
||||
#define CTR_TxFailed_MESSAGE "CTRE CAN Transmit Error"
|
||||
#define CTR_SigNotUpdated_MESSAGE "CTRE CAN Signal Not Updated"
|
||||
|
||||
#define NiFpga_Status_FifoTimeout_MESSAGE "NIFPGA: FIFO timeout error"
|
||||
#define NiFpga_Status_TransferAborted_MESSAGE "NIFPGA: Transfer aborted error"
|
||||
@@ -45,6 +47,8 @@
|
||||
#define ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE "HAL: Attempted to read AnalogTrigger pulse output."
|
||||
#define PARAMETER_OUT_OF_RANGE -1028
|
||||
#define PARAMETER_OUT_OF_RANGE_MESSAGE "HAL: A parameter is out of range."
|
||||
#define RESOURCE_IS_ALLOCATED -1029
|
||||
#define RESOURCE_IS_ALLOCATED_MESSAGE "HAL: Resource already allocated"
|
||||
|
||||
#define VI_ERROR_SYSTEM_ERROR_MESSAGE "HAL - VISA: System Error";
|
||||
#define VI_ERROR_INV_OBJECT_MESSAGE "HAL - VISA: Invalid Object"
|
||||
|
||||
@@ -230,7 +230,7 @@ extern "C"
|
||||
int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble);
|
||||
int HALGetMatchTime(float *matchTime);
|
||||
|
||||
void HALSetNewDataSem(pthread_cond_t *);
|
||||
void HALSetNewDataSem(MULTIWAIT_ID sem);
|
||||
|
||||
bool HALGetSystemActive(int32_t *status);
|
||||
bool HALGetBrownedOut(int32_t *status);
|
||||
|
||||
@@ -13,4 +13,9 @@ extern "C"
|
||||
|
||||
bool getSolenoid(void* solenoid_port_pointer, int32_t *status);
|
||||
void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status);
|
||||
|
||||
int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status);
|
||||
bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status);
|
||||
bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status);
|
||||
void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status);
|
||||
}
|
||||
|
||||
@@ -61,4 +61,56 @@ float getCompressorCurrent(void *pcm_pointer, int32_t *status) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorCurrentTooHighFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorCurrentTooHighStickyFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorShortedStickyFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getCompressorShortedFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorShortedFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorNotConnectedStickyFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
bool value;
|
||||
|
||||
*status = module->GetCompressorNotConnectedFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status) {
|
||||
PCM *module = (PCM *)pcm_pointer;
|
||||
|
||||
*status = module->ClearStickyFaults();
|
||||
}
|
||||
|
||||
@@ -384,7 +384,11 @@ bool allocateDIO(void* digital_port_pointer, bool input, int32_t *status) {
|
||||
DigitalPort* port = (DigitalPort*) digital_port_pointer;
|
||||
char buf[64];
|
||||
snprintf(buf, 64, "DIO %d", port->port.pin);
|
||||
if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) return false;
|
||||
if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) {
|
||||
*status = RESOURCE_IS_ALLOCATED;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
Synchronized sync(digitalDIOSemaphore);
|
||||
|
||||
@@ -420,7 +424,11 @@ bool allocatePWMChannel(void* digital_port_pointer, int32_t *status) {
|
||||
DigitalPort* port = (DigitalPort*) digital_port_pointer;
|
||||
char buf[64];
|
||||
snprintf(buf, 64, "PWM %d", port->port.pin);
|
||||
if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) return false;
|
||||
if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) {
|
||||
*status = RESOURCE_IS_ALLOCATED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (port->port.pin > tPWM::kNumHdrRegisters-1) {
|
||||
snprintf(buf, 64, "PWM %d and DIO %d", port->port.pin, remapMXPPWMChannel(port->port.pin) + 10);
|
||||
if (DIOChannels->Allocate(remapMXPPWMChannel(port->port.pin) + 10, buf) == ~0ul) return false;
|
||||
@@ -830,7 +838,7 @@ double getCounterPeriod(void* counter_pointer, int32_t *status) {
|
||||
// output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
|
||||
period = (double)(output.Period << 1) / (double)output.Count;
|
||||
}
|
||||
return period * 1.0e-6;
|
||||
return period * 2.5e-8; // result * timebase (currently 40ns)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,7 +850,7 @@ double getCounterPeriod(void* counter_pointer, int32_t *status) {
|
||||
*/
|
||||
void setCounterMaxPeriod(void* counter_pointer, double maxPeriod, int32_t *status) {
|
||||
Counter* counter = (Counter*) counter_pointer;
|
||||
counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 1.0e6), status);
|
||||
counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8), status);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -992,7 +1000,7 @@ double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
|
||||
// output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
|
||||
value = (double)(output.Period << 1) / (double)output.Count;
|
||||
}
|
||||
double measuredPeriod = value * 1.0e-6;
|
||||
double measuredPeriod = value * 2.5e-8;
|
||||
return measuredPeriod / DECODING_SCALING_FACTOR;
|
||||
}
|
||||
|
||||
@@ -1010,7 +1018,7 @@ double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
|
||||
*/
|
||||
void setEncoderMaxPeriod(void* encoder_pointer, double maxPeriod, int32_t *status) {
|
||||
Encoder* encoder = (Encoder*) encoder_pointer;
|
||||
encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 1.0e6 * DECODING_SCALING_FACTOR), status);
|
||||
encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8 * DECODING_SCALING_FACTOR), status);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1070,6 +1078,20 @@ uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status) {
|
||||
return encoder->encoder->readTimerConfig_AverageSize(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an index source for an encoder, which is an input that resets the
|
||||
* encoder's count.
|
||||
*/
|
||||
void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
|
||||
bool edgeSensitive, int32_t *status) {
|
||||
Encoder* encoder = (Encoder*) encoder_pointer;
|
||||
encoder->encoder->writeConfig_IndexSource_Channel((unsigned char)pin, status);
|
||||
encoder->encoder->writeConfig_IndexSource_Module((unsigned char)0, status);
|
||||
encoder->encoder->writeConfig_IndexSource_AnalogTrigger(analogTrigger, status);
|
||||
encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status);
|
||||
encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loop timing of the PWM system
|
||||
*
|
||||
|
||||
@@ -54,6 +54,10 @@ const char* getHALErrorMessage(int32_t code)
|
||||
return CTR_InvalidParamValue_MESSAGE;
|
||||
case CTR_UnexpectedArbId:
|
||||
return CTR_UnexpectedArbId_MESSAGE;
|
||||
case CTR_TxFailed:
|
||||
return CTR_TxFailed_MESSAGE;
|
||||
case CTR_SigNotUpdated:
|
||||
return CTR_SigNotUpdated_MESSAGE;
|
||||
case NiFpga_Status_FifoTimeout:
|
||||
return NiFpga_Status_FifoTimeout_MESSAGE;
|
||||
case NiFpga_Status_TransferAborted:
|
||||
@@ -223,9 +227,9 @@ int HALGetMatchTime(float *matchTime)
|
||||
return FRC_NetworkCommunication_getMatchTime(matchTime);
|
||||
}
|
||||
|
||||
void HALSetNewDataSem(pthread_cond_t * param)
|
||||
void HALSetNewDataSem(MULTIWAIT_ID sem)
|
||||
{
|
||||
setNewDataSem(param);
|
||||
setNewDataSem(sem);
|
||||
}
|
||||
|
||||
bool HALGetSystemActive(int32_t *status)
|
||||
|
||||
@@ -13,8 +13,10 @@ void serialInitializePort(uint8_t port, int32_t *status) {
|
||||
|
||||
if(port == 0)
|
||||
portName = "ASRL1::INSTR";
|
||||
else
|
||||
else if (port == 1)
|
||||
portName = "ASRL2::INSTR";
|
||||
else
|
||||
portName = "ASRL3::INSTR";
|
||||
|
||||
*status = viOpen(m_resourceManagerHandle, const_cast<char*>(portName), VI_NULL, VI_NULL, (ViSession*)&m_portHandle[port]);
|
||||
if(*status > 0)
|
||||
|
||||
@@ -52,3 +52,33 @@ void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status) {
|
||||
|
||||
port->module->SetSolenoid(port->pin, value);
|
||||
}
|
||||
|
||||
int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status){
|
||||
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
|
||||
UINT8 value;
|
||||
|
||||
*status = port->module->GetSolenoidBlackList(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status){
|
||||
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
|
||||
bool value;
|
||||
|
||||
*status = port->module->GetSolenoidStickyFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status){
|
||||
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
|
||||
bool value;
|
||||
|
||||
*status = port->module->GetSolenoidFault(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status){
|
||||
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
|
||||
|
||||
*status = port->module->ClearStickyFaults();
|
||||
}
|
||||
|
||||
@@ -63,9 +63,8 @@
|
||||
* ClearIaccum()
|
||||
* ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
|
||||
*
|
||||
* CloseLoopRampRate ramps the target of the close loop. The units are in position per 1ms. Set to zero to disable ramping.
|
||||
* So a value of 10 means allow the target input of the close loop to approach the user's demand by 10 units (ADC or encoder edges)
|
||||
* per 1ms.
|
||||
* CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
|
||||
* Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
|
||||
*
|
||||
* auto generated using spreadsheet and WpiClassGen.csproj
|
||||
* @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
|
||||
@@ -243,6 +242,11 @@ typedef struct _TALON_Param_Response_t {
|
||||
|
||||
CanTalonSRX::CanTalonSRX(int deviceNumber,int controlPeriodMs): CtreCanNode(deviceNumber), _can_h(0), _can_stat(0)
|
||||
{
|
||||
/* bound period to be within [1 ms,95 ms] */
|
||||
if(controlPeriodMs < 1)
|
||||
controlPeriodMs = 1;
|
||||
else if(controlPeriodMs > 95)
|
||||
controlPeriodMs = 95;
|
||||
RegisterRx(STATUS_1 | (UINT8)deviceNumber );
|
||||
RegisterRx(STATUS_2 | (UINT8)deviceNumber );
|
||||
RegisterRx(STATUS_3 | (UINT8)deviceNumber );
|
||||
@@ -567,6 +571,11 @@ CTR_Code CanTalonSRX::GetReverseSoftEnable(int & enable)
|
||||
CTR_Code CanTalonSRX::SetStatusFrameRate(unsigned frameEnum, unsigned periodMs)
|
||||
{
|
||||
int32_t status = 0;
|
||||
/* bounds check the period */
|
||||
if(periodMs < 1)
|
||||
periodMs = 1;
|
||||
else if (periodMs > 255)
|
||||
periodMs = 255;
|
||||
uint8_t period = (uint8_t)periodMs;
|
||||
/* tweak just the status messsage rate the caller cares about */
|
||||
switch(frameEnum){
|
||||
@@ -867,9 +876,9 @@ CTR_Code CanTalonSRX::GetAnalogInVel(int ¶m)
|
||||
raw |= rx->AnalogInVelH;
|
||||
raw <<= 8;
|
||||
raw |= rx->AnalogInVelL;
|
||||
param = (int)raw;
|
||||
raw <<= (32-16); /* sign extend */
|
||||
raw >>= (32-16); /* sign extend */
|
||||
param = (int)raw;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code CanTalonSRX::GetTemp(double ¶m)
|
||||
@@ -966,6 +975,23 @@ CTR_Code CanTalonSRX::SetModeSelect(int param)
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
/**
|
||||
* @param modeSelect selects which mode.
|
||||
* @param demand setpt or throttle or masterId to follow.
|
||||
* @return error code, 0 iff successful.
|
||||
* This function has the advantage of atomically setting mode and demand.
|
||||
*/
|
||||
CTR_Code CanTalonSRX::SetModeSelect(int modeSelect,int demand)
|
||||
{
|
||||
CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
|
||||
if (toFill.IsEmpty()) return CTR_UnexpectedArbId;
|
||||
toFill->ModeSelect = modeSelect;
|
||||
toFill->DemandH = demand>>16;
|
||||
toFill->DemandM = demand>>8;
|
||||
toFill->DemandL = demand>>0;
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
CTR_Code CanTalonSRX::SetProfileSlotSelect(int param)
|
||||
{
|
||||
CtreCanNode::txTask<TALON_Control_1_General_10ms_t> toFill = GetTx<TALON_Control_1_General_10ms_t>(CONTROL_1 | GetDeviceNumber());
|
||||
@@ -990,3 +1016,226 @@ CTR_Code CanTalonSRX::SetRevFeedbackSensor(int param)
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
//------------------ C interface --------------------------------------------//
|
||||
extern "C" {
|
||||
void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs)
|
||||
{
|
||||
return new CanTalonSRX(deviceNumber, controlPeriodMs);
|
||||
}
|
||||
void c_TalonSRX_Destroy(void *handle)
|
||||
{
|
||||
delete (CanTalonSRX*)handle;
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetParam((CanTalonSRX::param_t)paramEnum, value);
|
||||
}
|
||||
CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->RequestParam((CanTalonSRX::param_t)paramEnum);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetParamResponse((CanTalonSRX::param_t)paramEnum, *value);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetParamResponseInt32((CanTalonSRX::param_t)paramEnum, *value);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetStatusFrameRate(frameEnum, periodMs);
|
||||
}
|
||||
CTR_Code c_TalonSRX_ClearStickyFaults(void *handle)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->ClearStickyFaults();
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_OverTemp(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_UnderVoltage(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_ForLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_RevLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_HardwareFailure(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_ForSoftLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFault_RevSoftLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_OverTemp(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_UnderVoltage(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_ForLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_RevLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_ForSoftLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetStckyFault_RevSoftLim(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetAppliedThrottle(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetCloseLoopErr(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFeedbackDeviceSelect(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetModeSelect(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetLimitSwitchEn(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetLimitSwitchClosedFor(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetLimitSwitchClosedRev(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetSensorPosition(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetSensorVelocity(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetCurrent(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetBrakeIsEnabled(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetEncPosition(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetEncVel(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetEncIndexRiseEvents(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetQuadApin(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetQuadBpin(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetQuadIdxpin(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetAnalogInWithOv(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetAnalogInVel(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetTemp(void *handle, double *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetTemp(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetBatteryV(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetResetCount(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetResetFlags(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->GetFirmVers(*param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetDemand(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetDemand(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetOverrideLimitSwitchEn(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetFeedbackDeviceSelect(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetRevMotDuringCloseLoopEn(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetOverrideBrakeType(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetModeSelect(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetModeSelect(modeSelect, demand);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetProfileSlotSelect(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetRampThrottle(param);
|
||||
}
|
||||
CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param)
|
||||
{
|
||||
return ((CanTalonSRX*)handle)->SetRevFeedbackSensor(param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,8 @@
|
||||
* ClearIaccum()
|
||||
* ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
|
||||
*
|
||||
* CloseLoopRampRate ramps the target of the close loop. The units are in position per 1ms. Set to zero to disable ramping.
|
||||
* So a value of 10 means allow the target input of the close loop to approach the user's demand by 10 units (ADC or encoder edges)
|
||||
* per 1ms.
|
||||
* CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
|
||||
* Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
|
||||
*
|
||||
* auto generated using spreadsheet and WpiClassGen.csproj
|
||||
* @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
|
||||
@@ -212,6 +211,8 @@ public:
|
||||
eResetFlags=88,
|
||||
eFirmVers=89,
|
||||
eSettingsChanged=90,
|
||||
eQuadFilterEn=91,
|
||||
ePidIaccum=93,
|
||||
}param_t;
|
||||
/*---------------------setters and getters that use the solicated param request/response-------------*//**
|
||||
* Send a one shot frame to set an arbitrary signal.
|
||||
@@ -308,9 +309,67 @@ public:
|
||||
CTR_Code SetRevMotDuringCloseLoopEn(int param);
|
||||
CTR_Code SetOverrideBrakeType(int param);
|
||||
CTR_Code SetModeSelect(int param);
|
||||
CTR_Code SetModeSelect(int modeSelect,int demand);
|
||||
CTR_Code SetProfileSlotSelect(int param);
|
||||
CTR_Code SetRampThrottle(int param);
|
||||
CTR_Code SetRevFeedbackSensor(int param);
|
||||
};
|
||||
extern "C" {
|
||||
void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs);
|
||||
void c_TalonSRX_Destroy(void *handle);
|
||||
CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value);
|
||||
CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum);
|
||||
CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value);
|
||||
CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value);
|
||||
CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs);
|
||||
CTR_Code c_TalonSRX_ClearStickyFaults(void *handle);
|
||||
CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetTemp(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_SetDemand(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand);
|
||||
CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ static const INT32 kCANPeriod = 20;
|
||||
#define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg<PcmStatusFault_t> rx = GetRx<PcmStatusFault_t> (STATUS_SOL_FAULTS,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
#define GET_PCM_DEBUG() CtreCanNode::recMsg<PcmDebug_t> rx = GetRx<PcmDebug_t> (STATUS_DEBUG,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
|
||||
#define CONTROL_1 0x09041C00
|
||||
#define CONTROL_1 0x09041C00 /* PCM_Control */
|
||||
#define CONTROL_2 0x09041C40 /* PCM_SupplemControl */
|
||||
#define CONTROL_3 0x09041C80 /* PcmControlSetOneShotDur_t */
|
||||
|
||||
/* encoder/decoders */
|
||||
typedef struct _PcmStatus_t{
|
||||
@@ -27,8 +29,8 @@ typedef struct _PcmStatus_t{
|
||||
unsigned compressorOn:1;
|
||||
unsigned stickyFaultFuseTripped:1;
|
||||
unsigned stickyFaultCompCurrentTooHigh:1;
|
||||
unsigned faultCompCurrentTooHigh:1;
|
||||
unsigned faultFuseTripped:1;
|
||||
unsigned faultCompCurrentTooHigh:1;
|
||||
unsigned faultHardwareFailure:1;
|
||||
unsigned isCloseloopEnabled:1;
|
||||
unsigned pressureSwitchEn:1;
|
||||
@@ -40,7 +42,8 @@ typedef struct _PcmStatus_t{
|
||||
unsigned compressorCurrentTop6:6;
|
||||
unsigned solenoidVoltageBtm2:2;
|
||||
/* Byte 5 */
|
||||
unsigned reserved:2;
|
||||
unsigned StickyFault_dItooHigh :1;
|
||||
unsigned Fault_dItooHigh :1;
|
||||
unsigned moduleEnabled:1;
|
||||
unsigned closedLoopOutput:1;
|
||||
unsigned compressorCurrentBtm4:4;
|
||||
@@ -63,19 +66,28 @@ typedef struct _PcmControl_t{
|
||||
unsigned compressorOn:1;
|
||||
unsigned closedLoopEnable:1;
|
||||
unsigned clearStickyFaults:1;
|
||||
/* Byte 4 */
|
||||
unsigned OneShotField_h8:8;
|
||||
/* Byte 5 */
|
||||
unsigned OneShotField_l8:8;
|
||||
}PcmControl_t;
|
||||
|
||||
typedef struct _PcmControlSetOneShotDur_t{
|
||||
uint8_t sol10MsPerUnit[8];
|
||||
}PcmControlSetOneShotDur_t;
|
||||
|
||||
typedef struct _PcmStatusFault_t{
|
||||
/* Byte 0 */
|
||||
unsigned SolenoidBlacklist:8;
|
||||
/* Byte 1 */
|
||||
unsigned reserved1:8;
|
||||
unsigned reserved2:8;
|
||||
unsigned reserved3:8;
|
||||
unsigned reserved4:8;
|
||||
unsigned reserved5:8;
|
||||
unsigned reserved6:8;
|
||||
unsigned reserved7:8;
|
||||
unsigned reserved_bit0 :1;
|
||||
unsigned reserved_bit1 :1;
|
||||
unsigned reserved_bit2 :1;
|
||||
unsigned reserved_bit3 :1;
|
||||
unsigned StickyFault_CompNoCurrent :1;
|
||||
unsigned Fault_CompNoCurrent :1;
|
||||
unsigned StickyFault_SolenoidJumper :1;
|
||||
unsigned Fault_SolenoidJumper :1;
|
||||
}PcmStatusFault_t;
|
||||
|
||||
typedef struct _PcmDebug_t{
|
||||
@@ -135,12 +147,13 @@ CTR_Code PCM::SetSolenoid(unsigned char idx, bool en)
|
||||
*
|
||||
* @Param - clr - Clear / do not clear faults
|
||||
*/
|
||||
CTR_Code PCM::ClearStickyFaults(bool clr)
|
||||
CTR_Code PCM::ClearStickyFaults()
|
||||
{
|
||||
CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
|
||||
if(toFill.IsEmpty())return CTR_UnexpectedArbId;
|
||||
toFill->clearStickyFaults = clr;
|
||||
FlushTx(toFill);
|
||||
int32_t status = 0;
|
||||
uint8_t pcmSupplemControl[] = { 0, 0, 0, 0x80 }; /* only bit set is ClearStickyFaults */
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), pcmSupplemControl, sizeof(pcmSupplemControl), 0, &status);
|
||||
if(status)
|
||||
return CTR_TxFailed;
|
||||
return CTR_OKAY;
|
||||
}
|
||||
|
||||
@@ -158,6 +171,59 @@ CTR_Code PCM::SetClosedLoopControl(bool en)
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
/* Get solenoid Blacklist status
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
|
||||
*/
|
||||
CTR_Code PCM::FireOneShotSolenoid(UINT8 idx)
|
||||
{
|
||||
CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
|
||||
if(toFill.IsEmpty())return CTR_UnexpectedArbId;
|
||||
/* grab field as it is now */
|
||||
uint16_t oneShotField;
|
||||
oneShotField = toFill->OneShotField_h8;
|
||||
oneShotField <<= 8;
|
||||
oneShotField |= toFill->OneShotField_l8;
|
||||
/* get the caller's channel */
|
||||
uint16_t shift = 2*idx;
|
||||
uint16_t mask = 3; /* two bits wide */
|
||||
uint8_t chBits = (oneShotField >> shift) & mask;
|
||||
/* flip it */
|
||||
chBits = (chBits)%3 + 1;
|
||||
/* clear out 2bits for this channel*/
|
||||
oneShotField &= ~(mask << shift);
|
||||
/* put new field in */
|
||||
oneShotField |= chBits << shift;
|
||||
/* apply field as it is now */
|
||||
toFill->OneShotField_h8 = oneShotField >> 8;
|
||||
toFill->OneShotField_l8 = oneShotField;
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
/* Configure the pulse width of a solenoid channel for one-shot pulse.
|
||||
* Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - idx - ID of solenoid [0,7] to configure.
|
||||
* @Param - durMs - pulse width in ms.
|
||||
*/
|
||||
CTR_Code PCM::SetOneShotDurationMs(UINT8 idx,uint32_t durMs)
|
||||
{
|
||||
/* sanity check caller's param */
|
||||
if(idx > 8)
|
||||
return CTR_InvalidParamValue;
|
||||
/* get latest tx frame */
|
||||
CtreCanNode::txTask<PcmControlSetOneShotDur_t> toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
|
||||
if(toFill.IsEmpty()){
|
||||
/* only send this out if caller wants to do one-shots */
|
||||
RegisterTx(CONTROL_3 | _deviceNumber, kCANPeriod);
|
||||
/* grab it */
|
||||
toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
|
||||
}
|
||||
toFill->sol10MsPerUnit[idx] = std::min(durMs/10,(uint32_t)0xFF);
|
||||
/* apply the new data bytes */
|
||||
FlushTx(toFill);
|
||||
return CTR_OKAY;
|
||||
}
|
||||
|
||||
/* Get solenoid state
|
||||
*
|
||||
@@ -248,12 +314,36 @@ CTR_Code PCM::GetHardwareFault(bool &status)
|
||||
*
|
||||
* @Return - True/False - True if shorted compressor detected, false if otherwise
|
||||
*/
|
||||
CTR_Code PCM::GetCompressorFault(bool &status)
|
||||
CTR_Code PCM::GetCompressorCurrentTooHighFault(bool &status)
|
||||
{
|
||||
GET_PCM_STATUS();
|
||||
status = rx->faultCompCurrentTooHigh;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PCM::GetCompressorShortedStickyFault(bool &status)
|
||||
{
|
||||
GET_PCM_STATUS();
|
||||
status = rx->StickyFault_dItooHigh;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PCM::GetCompressorShortedFault(bool &status)
|
||||
{
|
||||
GET_PCM_STATUS();
|
||||
status = rx->Fault_dItooHigh;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PCM::GetCompressorNotConnectedStickyFault(bool &status)
|
||||
{
|
||||
GET_PCM_SOL_FAULTS();
|
||||
status = rx->StickyFault_CompNoCurrent;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PCM::GetCompressorNotConnectedFault(bool &status)
|
||||
{
|
||||
GET_PCM_SOL_FAULTS();
|
||||
status = rx->Fault_CompNoCurrent;
|
||||
return rx.err;
|
||||
}
|
||||
|
||||
/* Get solenoid fault value
|
||||
*
|
||||
@@ -271,7 +361,7 @@ CTR_Code PCM::GetSolenoidFault(bool &status)
|
||||
* @Return - True/False - True if solenoid had previously been shorted
|
||||
* (and sticky fault was not cleared), false if otherwise
|
||||
*/
|
||||
CTR_Code PCM::GetCompressorStickyFault(bool &status)
|
||||
CTR_Code PCM::GetCompressorCurrentTooHighStickyFault(bool &status)
|
||||
{
|
||||
GET_PCM_STATUS();
|
||||
status = rx->stickyFaultCompCurrentTooHigh;
|
||||
@@ -369,7 +459,7 @@ extern "C" {
|
||||
return ((PCM*) handle)->SetClosedLoopControl(param);
|
||||
}
|
||||
CTR_Code c_ClearStickyFaults(void * handle, INT8 param) {
|
||||
return ((PCM*) handle)->ClearStickyFaults(param);
|
||||
return ((PCM*) handle)->ClearStickyFaults();
|
||||
}
|
||||
CTR_Code c_GetSolenoid(void * handle, UINT8 idx, INT8 * status) {
|
||||
bool bstatus;
|
||||
@@ -410,7 +500,7 @@ extern "C" {
|
||||
}
|
||||
CTR_Code c_GetCompressorFault(void * handle, INT8*status) {
|
||||
bool bstatus;
|
||||
CTR_Code retval = ((PCM*) handle)->GetCompressorFault(bstatus);
|
||||
CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighFault(bstatus);
|
||||
*status = bstatus;
|
||||
return retval;
|
||||
}
|
||||
@@ -422,7 +512,7 @@ extern "C" {
|
||||
}
|
||||
CTR_Code c_GetCompressorStickyFault(void * handle, INT8*status) {
|
||||
bool bstatus;
|
||||
CTR_Code retval = ((PCM*) handle)->GetCompressorStickyFault(bstatus);
|
||||
CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighStickyFault(bstatus);
|
||||
*status = bstatus;
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,8 @@ public:
|
||||
|
||||
/* Clears PCM sticky faults (indicators of past faults
|
||||
* @Return - CTR_Code - Error code (if any) for setting solenoid
|
||||
* @Param - clr - Clear / do not clear faults
|
||||
*/
|
||||
CTR_Code ClearStickyFaults(bool clr);
|
||||
CTR_Code ClearStickyFaults();
|
||||
|
||||
/* Get solenoid state
|
||||
*
|
||||
@@ -76,9 +75,9 @@ public:
|
||||
|
||||
/* Get compressor fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - status - True if shorted compressor detected, false if otherwise
|
||||
* @Param - status - True if abnormally high compressor current detected, false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorFault(bool &status);
|
||||
CTR_Code GetCompressorCurrentTooHighFault(bool &status);
|
||||
|
||||
/* Get solenoid fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
@@ -91,7 +90,29 @@ public:
|
||||
* @Param - status - True if solenoid had previously been shorted
|
||||
* (and sticky fault was not cleared), false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorStickyFault(bool &status);
|
||||
CTR_Code GetCompressorCurrentTooHighStickyFault(bool &status);
|
||||
/* Get compressor shorted sticky fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - status - True if compressor output is shorted, false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorShortedStickyFault(bool &status);
|
||||
/* Get compressor shorted fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - status - True if compressor output is shorted, false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorShortedFault(bool &status);
|
||||
/* Get compressor is not connected sticky fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - status - True if compressor current is too low,
|
||||
* indicating compressor is not connected, false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorNotConnectedStickyFault(bool &status);
|
||||
/* Get compressor is not connected fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - status - True if compressor current is too low,
|
||||
* indicating compressor is not connected, false if otherwise
|
||||
*/
|
||||
CTR_Code GetCompressorNotConnectedFault(bool &status);
|
||||
|
||||
/* Get solenoid sticky fault value
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
@@ -146,6 +167,21 @@ public:
|
||||
* @Param - status - Returns TRUE if PCM is enabled, FALSE if disabled
|
||||
*/
|
||||
CTR_Code isModuleEnabled(bool &status);
|
||||
|
||||
/* Get solenoid Blacklist status
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
|
||||
*/
|
||||
CTR_Code FireOneShotSolenoid(UINT8 idx);
|
||||
|
||||
/* Configure the pulse width of a solenoid channel for one-shot pulse.
|
||||
* Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
* @Param - idx - ID of solenoid [0,7] to configure.
|
||||
* @Param - durMs - pulse width in ms.
|
||||
*/
|
||||
CTR_Code SetOneShotDurationMs(UINT8 idx,uint32_t durMs);
|
||||
|
||||
};
|
||||
//------------------ C interface --------------------------------------------//
|
||||
extern "C" {
|
||||
|
||||
@@ -178,7 +178,8 @@ CTR_Code PDP::GetTotalEnergy(double &energyJoules)
|
||||
raw <<= 8;
|
||||
raw |= rx->Energy_125mWPerUnitXTmeas_l8;
|
||||
energyJoules = 0.125 * raw; /* mW integrated every TmeasMs */
|
||||
energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
|
||||
energyJoules *= 0.001; /* convert from mW to W */
|
||||
energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
|
||||
return rx.err;
|
||||
}
|
||||
/* Clear sticky faults.
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
#ifndef __I2C_LIB_H__
|
||||
#define __I2C_LIB_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int i2clib_open(const char *device);
|
||||
void i2clib_close(int handle);
|
||||
int i2clib_read(int handle, uint8_t dev_addr, char *recv_buf, int32_t recv_size);
|
||||
int i2clib_write(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size);
|
||||
int i2clib_writeread(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size, char *recv_buf, int32_t recv_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __I2C_LIB_H__ */
|
||||
@@ -1,12 +1,9 @@
|
||||
#ifndef __SPI_LIB_H__
|
||||
#define __SPI_LIB_H__
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int spilib_open(const char *device);
|
||||
void spilib_close(int handle);
|
||||
int spilib_setspeed(int handle, uint32_t speed);
|
||||
@@ -15,11 +12,8 @@ int spilib_setopts(int handle, int msb_first, int sample_on_trailing, int clk_id
|
||||
int spilib_read(int handle, char *recv_buf, int32_t size);
|
||||
int spilib_write(int handle, const char *send_buf, int32_t size);
|
||||
int spilib_writeread(int handle, const char *send_buf, char *recv_buf, int32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_LIB_H__ */
|
||||
|
||||
|
||||
#endif /* __SPI_LIB_H__ */
|
||||
@@ -234,7 +234,7 @@ TCL_SUBST =
|
||||
# members will be omitted, etc.
|
||||
# The default value is: NO.
|
||||
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
|
||||
# Python sources only. Doxygen will then generate output that is more tailored
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
|
||||
class AbstractNetworkTableEntryStore : public IncomingEntryReceiver{
|
||||
protected:
|
||||
|
||||
NTReentrantSemaphore block_namedEntries;
|
||||
std::map<EntryId,NetworkTableEntry*> idEntries;
|
||||
std::map<std::string,NetworkTableEntry*> namedEntries;
|
||||
TableListenerManager& listenerManager;
|
||||
@@ -45,10 +47,10 @@ protected:
|
||||
OutgoingEntryReceiver* incomingReceiver;
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool addEntry(NetworkTableEntry* entry) = 0;
|
||||
virtual bool updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) = 0;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~AbstractNetworkTableEntryStore();
|
||||
|
||||
@@ -62,14 +64,14 @@ public:
|
||||
|
||||
void SetOutgoingReceiver(OutgoingEntryReceiver* receiver);
|
||||
void SetIncomingReceiver(OutgoingEntryReceiver* receiver);
|
||||
|
||||
|
||||
void PutOutgoing(std::string& name, NetworkTableEntryType* type, EntryValue value);
|
||||
void PutOutgoing(NetworkTableEntry* tableEntry, EntryValue value);
|
||||
|
||||
|
||||
|
||||
void offerIncomingAssignment(NetworkTableEntry* entry);
|
||||
void offerIncomingUpdate(NetworkTableEntry* entry, EntryId sequenceNumber, EntryValue value);
|
||||
|
||||
|
||||
void notifyEntries(ITable* table, ITableListener* listener);
|
||||
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
|
||||
NetworkTableEntry(std::string& name, NetworkTableEntryType* type, EntryValue value);
|
||||
NetworkTableEntry(EntryId id, std::string& name, SequenceNumber sequenceNumber, NetworkTableEntryType* type, EntryValue value);
|
||||
NetworkTableEntry(const NetworkTableEntry &);
|
||||
virtual ~NetworkTableEntry();
|
||||
|
||||
EntryId GetId();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <iolib.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -24,6 +25,13 @@ FDIOStream::FDIOStream(int _fd){
|
||||
// f = fdopen(_fd, "rbwb");
|
||||
// if(f==NULL)
|
||||
// throw IOException("Could not open stream from file descriptor", errno);
|
||||
// Set the TCP socket to be non-blocking
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||
{
|
||||
::close(fd);
|
||||
throw IOException("Could not set socket to non-blocking mode");
|
||||
}
|
||||
}
|
||||
FDIOStream::~FDIOStream(){
|
||||
close();
|
||||
@@ -66,14 +74,35 @@ int FDIOStream::read(void* ptr, int numbytes){
|
||||
return totalRead;
|
||||
}
|
||||
int FDIOStream::write(const void* ptr, int numbytes){
|
||||
int numWrote = ::write(fd, (char*)ptr, numbytes);//TODO: this is bad
|
||||
//int numWrote = fwrite(ptr, 1, numbytes, f);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
perror("write error: ");
|
||||
fflush(stderr);
|
||||
throw IOException("Could not write all bytes to fd stream");
|
||||
|
||||
int numWrote = ::write(fd, (char*)ptr, numbytes);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
|
||||
if (numWrote == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
|
||||
{
|
||||
// see if write timeout expires
|
||||
struct timeval timeout;
|
||||
fd_set fdSet;
|
||||
|
||||
FD_ZERO(&fdSet);
|
||||
FD_SET(fd, &fdSet);
|
||||
timeout.tv_sec = 1; // wait 1 second for the other side to connect
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int select_result = select(FD_SETSIZE, NULL, &fdSet, NULL, &timeout);
|
||||
if ( select_result < 0)
|
||||
throw IOException("Select returned an error on write");
|
||||
|
||||
if (FD_ISSET(fd, &fdSet)) {
|
||||
numWrote = ::write(fd, (char*)ptr, numbytes);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
}
|
||||
}
|
||||
|
||||
perror("write error: ");
|
||||
fflush(stderr);
|
||||
throw IOException("Could not write all bytes to fd stream");
|
||||
}
|
||||
void FDIOStream::flush(){
|
||||
//if(fflush(f)==EOF)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "OSAL/Task.h"
|
||||
|
||||
#include "NetworkCommunication/UsageReporting.h"
|
||||
#include "WPIErrors.h"
|
||||
#include <string.h>
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -55,7 +57,7 @@ DWORD thread_proc( void *p_ptr )
|
||||
//This sets the name of the thread, which can help to identify threads in win32
|
||||
#define MS_VC_EXCEPTION 0x406D1388
|
||||
static void set_thread_name( const char *p_thread_name, DWORD ID )
|
||||
{
|
||||
{
|
||||
#pragma pack(push,8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{ DWORD dwType; // Must be 0x1000.
|
||||
@@ -162,7 +164,7 @@ bool NTTask::Stop()
|
||||
::TerminateThread( m_Handle, 0 );
|
||||
|
||||
// Free the memory
|
||||
::VirtualFree( Info_.AllocationBase, 0, MEM_RELEASE );
|
||||
::VirtualFree( Info_.AllocationBase, 0, MEM_RELEASE );
|
||||
}
|
||||
|
||||
//if (Verify())
|
||||
@@ -289,19 +291,19 @@ bool NTTask::HandleError(STATUS results)
|
||||
//case S_objLib_OBJ_ID_ERROR:
|
||||
// wpi_setWPIErrorWithContext(TaskIDError, m_taskName);
|
||||
// break;
|
||||
//
|
||||
//
|
||||
//case S_objLib_OBJ_DELETED:
|
||||
// wpi_setWPIErrorWithContext(TaskDeletedError, m_taskName);
|
||||
// break;
|
||||
//
|
||||
//
|
||||
//case S_taskLib_ILLEGAL_OPTIONS:
|
||||
// wpi_setWPIErrorWithContext(TaskOptionsError, m_taskName);
|
||||
// break;
|
||||
//
|
||||
//
|
||||
//case S_memLib_NOT_ENOUGH_MEMORY:
|
||||
// wpi_setWPIErrorWithContext(TaskMemoryError, m_taskName);
|
||||
// break;
|
||||
//
|
||||
//
|
||||
//case S_taskLib_ILLEGAL_PRIORITY:
|
||||
// wpi_setWPIErrorWithContext(TaskPriorityError, m_taskName);
|
||||
// break;
|
||||
@@ -312,4 +314,3 @@ bool NTTask::HandleError(STATUS results)
|
||||
//}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
|
||||
FDIOStream::FDIOStream(int _fd){
|
||||
fd = _fd;
|
||||
// Set the TCP socket to be non-blocking
|
||||
u_long on = 1;
|
||||
if (ioctlsocket(fd, FIONBIO, &on))
|
||||
{
|
||||
::close(fd);
|
||||
throw IOException("Could not set socket to non-blocking mode");
|
||||
}
|
||||
}
|
||||
|
||||
FDIOStream::~FDIOStream(){
|
||||
@@ -79,13 +86,35 @@ int Send( int sockfd,char* Data, size_t sizeData )
|
||||
|
||||
int FDIOStream::write(const void* ptr, int numbytes)
|
||||
{
|
||||
int numWrote = Send(fd,(char *)ptr,numbytes);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
perror("write error: ");
|
||||
fflush(stderr);
|
||||
throw IOException("Could not write all bytes to fd stream");
|
||||
|
||||
int numWrote = ::write(fd, (char*)ptr, numbytes);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
|
||||
if (numWrote == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
|
||||
{
|
||||
// see if write timeout expires
|
||||
struct timeval timeout;
|
||||
fd_set fdSet;
|
||||
|
||||
FD_ZERO(&fdSet);
|
||||
FD_SET(fd, &fdSet);
|
||||
timeout.tv_sec = 1; // wait 1 second for the other side to connect
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int select_result = select(FD_SETSIZE, NULL, &fdSet, NULL, &timeout);
|
||||
if ( select_result < 0)
|
||||
throw IOException("Select returned an error on write");
|
||||
|
||||
if (FD_ISSET(fd, &fdSet)) {
|
||||
numWrote = ::write(fd, (char*)ptr, numbytes);
|
||||
if(numWrote==numbytes)
|
||||
return numWrote;
|
||||
}
|
||||
}
|
||||
|
||||
perror("write error: ");
|
||||
fflush(stderr);
|
||||
throw IOException("Could not write all bytes to fd stream");
|
||||
}
|
||||
|
||||
void FDIOStream::flush(){
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
AbstractNetworkTableEntryStore::AbstractNetworkTableEntryStore(TableListenerManager& lstnManager):
|
||||
AbstractNetworkTableEntryStore::AbstractNetworkTableEntryStore(TableListenerManager& lstnManager):
|
||||
listenerManager(lstnManager){
|
||||
}
|
||||
|
||||
|
||||
AbstractNetworkTableEntryStore::~AbstractNetworkTableEntryStore(){
|
||||
LOCK.take();
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
@@ -24,15 +24,15 @@
|
||||
namedEntries.erase(itr++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an entry based on it's name
|
||||
* @param name the name of the entry to look for
|
||||
* @return the entry or null if the entry does not exist
|
||||
*/
|
||||
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(std::string& name){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator value_itr = namedEntries.find(name);
|
||||
if(value_itr != namedEntries.end()) {
|
||||
return value_itr->second;
|
||||
@@ -40,11 +40,11 @@
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(EntryId entryId){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
|
||||
std::map<EntryId, NetworkTableEntry*>::iterator value_itr = idEntries.find(entryId);
|
||||
if(value_itr != idEntries.end()) {
|
||||
return value_itr->second;
|
||||
@@ -52,74 +52,74 @@
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string>* AbstractNetworkTableEntryStore::keys(){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::vector<std::string>* keys = new std::vector<std::string>();
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
|
||||
|
||||
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
{
|
||||
std::string key = (*itr).first;
|
||||
keys->push_back(key);
|
||||
}
|
||||
|
||||
|
||||
return (keys);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all entries
|
||||
* NOTE: This method should not be used with applications which cache entries which would lead to unknown results
|
||||
* This method is for use in testing only
|
||||
*/
|
||||
void AbstractNetworkTableEntryStore::clearEntries(){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
namedEntries.clear();
|
||||
idEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* clear the id's of all entries
|
||||
*/
|
||||
void AbstractNetworkTableEntryStore::clearIds(){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
idEntries.clear();
|
||||
|
||||
|
||||
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
{
|
||||
((NetworkTableEntry*)(*itr).second)->ClearId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AbstractNetworkTableEntryStore::SetOutgoingReceiver(OutgoingEntryReceiver* receiver){
|
||||
outgoingReceiver = receiver;
|
||||
}
|
||||
|
||||
|
||||
void AbstractNetworkTableEntryStore::SetIncomingReceiver(OutgoingEntryReceiver* receiver){
|
||||
incomingReceiver = receiver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores the given value under the given name and queues it for
|
||||
* Stores the given value under the given name and queues it for
|
||||
* transmission to the server.
|
||||
*
|
||||
*
|
||||
* @param name The name under which to store the given value.
|
||||
* @param type The type of the given value.
|
||||
* @param value The value to store.
|
||||
* @throws TableKeyExistsWithDifferentTypeException Thrown if an
|
||||
* @throws TableKeyExistsWithDifferentTypeException Thrown if an
|
||||
* entry already exists with the given name and is of a different type.
|
||||
*/
|
||||
void AbstractNetworkTableEntryStore::PutOutgoing(std::string& name, NetworkTableEntryType* type, EntryValue value){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator index = namedEntries.find(name);
|
||||
NetworkTableEntry* tableEntry;
|
||||
if(index == namedEntries.end())//if the name does not exist in the current entries
|
||||
@@ -137,21 +137,21 @@
|
||||
if(tableEntry->GetType()->id != type->id){
|
||||
throw TableKeyExistsWithDifferentTypeException(name, tableEntry->GetType());
|
||||
}
|
||||
|
||||
|
||||
EntryValue oldValue = tableEntry->GetValue();
|
||||
if(!type->areEqual(value, oldValue)){
|
||||
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
|
||||
outgoingReceiver->offerOutgoingUpdate(tableEntry);
|
||||
}
|
||||
|
||||
|
||||
tableEntry->FireListener(listenerManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AbstractNetworkTableEntryStore::PutOutgoing(NetworkTableEntry* tableEntry, EntryValue value){
|
||||
{
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
NetworkTableEntryType* type = tableEntry->GetType();
|
||||
EntryValue oldValue = tableEntry->GetValue();
|
||||
@@ -159,15 +159,15 @@
|
||||
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
|
||||
outgoingReceiver->offerOutgoingUpdate(tableEntry);
|
||||
}
|
||||
|
||||
|
||||
tableEntry->FireListener(listenerManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AbstractNetworkTableEntryStore::offerIncomingAssignment(NetworkTableEntry* entry){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr = namedEntries.find(entry->name);
|
||||
NetworkTableEntry* tableEntry;
|
||||
if(addEntry(entry)){
|
||||
@@ -177,7 +177,7 @@
|
||||
else{
|
||||
tableEntry = entry;
|
||||
}
|
||||
|
||||
|
||||
tableEntry->FireListener(listenerManager);//if we didnt have a pointer, then the copy of the version in the list would call this method, however with the pointer we are updating the version in the list
|
||||
incomingReceiver->offerOutgoingAssignment(tableEntry);
|
||||
}
|
||||
@@ -185,9 +185,9 @@
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AbstractNetworkTableEntryStore::offerIncomingUpdate(NetworkTableEntry* entry, SequenceNumber squenceNumber, EntryValue value){
|
||||
{
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
if(updateEntry(entry, squenceNumber, value)){
|
||||
entry->FireListener(listenerManager);
|
||||
@@ -195,15 +195,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called to say that a listener should notify the listener manager of all of the entries
|
||||
* @param listener
|
||||
* @param table
|
||||
* @param table
|
||||
*/
|
||||
void AbstractNetworkTableEntryStore::notifyEntries(ITable* table, ITableListener* listener){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
{
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,17 @@ NetworkTableEntry::NetworkTableEntry(EntryId _id, std::string& _name, SequenceNu
|
||||
m_isDirty = false;
|
||||
}
|
||||
|
||||
NetworkTableEntry::NetworkTableEntry(const NetworkTableEntry &other) :
|
||||
name(other.name),
|
||||
id(other.id),
|
||||
sequenceNumber(other.sequenceNumber),
|
||||
type(other.type),
|
||||
m_isNew(other.m_isNew),
|
||||
m_isDirty(other.m_isDirty)
|
||||
{
|
||||
value = type->copyValue(other.value);
|
||||
}
|
||||
|
||||
NetworkTableEntry::~NetworkTableEntry(){
|
||||
type->deleteValue(value);
|
||||
}
|
||||
|
||||
@@ -54,10 +54,15 @@ void WriteManager::stop(){
|
||||
|
||||
void WriteManager::offerOutgoingAssignment(NetworkTableEntry* entry) {
|
||||
{
|
||||
NTSynchronized sync(transactionsLock);
|
||||
((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->push(entry);
|
||||
|
||||
if(((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->size()>=queueSize){
|
||||
bool test_queue_overflow=false;
|
||||
{
|
||||
NTSynchronized sync(transactionsLock);
|
||||
((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->push(entry);
|
||||
|
||||
test_queue_overflow=(((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->size()>=queueSize);
|
||||
}
|
||||
if (test_queue_overflow)
|
||||
{
|
||||
run();
|
||||
writeWarning("assignment queue overflowed. decrease the rate at which you create new entries or increase the write buffer size");
|
||||
}
|
||||
@@ -67,9 +72,15 @@ void WriteManager::offerOutgoingAssignment(NetworkTableEntry* entry) {
|
||||
|
||||
void WriteManager::offerOutgoingUpdate(NetworkTableEntry* entry) {
|
||||
{
|
||||
NTSynchronized sync(transactionsLock);
|
||||
((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->push(entry);
|
||||
if(((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->size()>=queueSize){
|
||||
bool test_queue_overflow=false;
|
||||
{
|
||||
NTSynchronized sync(transactionsLock);
|
||||
((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->push(entry);
|
||||
test_queue_overflow=(((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->size()>=queueSize);
|
||||
|
||||
}
|
||||
if (test_queue_overflow)
|
||||
{
|
||||
run();
|
||||
writeWarning("update queue overflowed. decrease the rate at which you update entries or increase the write buffer size");
|
||||
}
|
||||
@@ -97,10 +108,17 @@ void WriteManager::run() {
|
||||
entry = ((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->front();
|
||||
((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->pop();
|
||||
{
|
||||
NTSynchronized sync(entryStore.LOCK);
|
||||
entry->MakeClean();
|
||||
wrote = true;
|
||||
receiver.offerOutgoingAssignment(entry);
|
||||
NetworkTableEntry * entryCopy;
|
||||
|
||||
{
|
||||
NTSynchronized sync(entryStore.LOCK);
|
||||
entry->MakeClean();
|
||||
wrote = true;
|
||||
entryCopy = new NetworkTableEntry(*entry);
|
||||
}
|
||||
|
||||
receiver.offerOutgoingAssignment(entryCopy);
|
||||
delete entryCopy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,10 +126,17 @@ void WriteManager::run() {
|
||||
entry = ((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->front();
|
||||
((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->pop();
|
||||
{
|
||||
NTSynchronized sync(entryStore.LOCK);
|
||||
entry->MakeClean();
|
||||
wrote = true;
|
||||
receiver.offerOutgoingUpdate(entry);
|
||||
NetworkTableEntry * entryCopy;
|
||||
|
||||
{
|
||||
NTSynchronized sync(entryStore.LOCK);
|
||||
entry->MakeClean();
|
||||
wrote = true;
|
||||
entryCopy = new NetworkTableEntry(*entry);
|
||||
}
|
||||
|
||||
receiver.offerOutgoingUpdate(entryCopy);
|
||||
delete entryCopy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ ClientNetworkTableEntryStore::~ClientNetworkTableEntryStore(){}
|
||||
|
||||
bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
NetworkTableEntry* entry = (NetworkTableEntry*)namedEntries[newEntry->name];
|
||||
|
||||
if(entry!=NULL){
|
||||
@@ -28,7 +28,7 @@ bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
|
||||
idEntries[newEntry->GetId()] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
entry->ForcePut(newEntry->GetSequenceNumber(), newEntry->GetType(), newEntry->GetValue());
|
||||
}
|
||||
else{
|
||||
@@ -41,7 +41,7 @@ bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
|
||||
}
|
||||
|
||||
bool ClientNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) {
|
||||
{
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
entry->ForcePut(sequenceNumber, value);
|
||||
if(entry->GetId()==NetworkTableEntry::UNKNOWN_ID){
|
||||
@@ -57,7 +57,7 @@ bool ClientNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, Sequenc
|
||||
* @throws IOException
|
||||
*/
|
||||
void ClientNetworkTableEntryStore::sendUnknownEntries(NetworkTableConnection& connection) {
|
||||
{
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* An abstraction for the NetworkTable protocol
|
||||
*
|
||||
*
|
||||
* @author mwills
|
||||
*
|
||||
*/
|
||||
@@ -19,6 +19,7 @@ NetworkTableConnection::~NetworkTableConnection(){
|
||||
|
||||
void NetworkTableConnection::SetIOStream(IOStream* stream)
|
||||
{
|
||||
isValid=(stream!=NULL);
|
||||
ioStream->SetIOStream(stream); //just passing through
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ void NetworkTableConnection::sendEntryUpdate(NetworkTableEntry& entry) {
|
||||
}
|
||||
|
||||
void NetworkTableConnection::read(ConnectionAdapter& adapter) {
|
||||
int messageType = ioStream->readByte();
|
||||
int messageType = (isValid)?ioStream->readByte():(int)KEEP_ALIVE;
|
||||
switch (messageType) {
|
||||
case KEEP_ALIVE:
|
||||
adapter.keepAlive();
|
||||
|
||||
@@ -17,10 +17,10 @@ NetworkTableServer::NetworkTableServer(IOStreamProvider& _streamProvider, Networ
|
||||
connectionList(&incomingStreamMonitor),
|
||||
writeManager(connectionList, threadManager, GetEntryStore(), ULONG_MAX),
|
||||
continuingReceiver(writeManager){
|
||||
|
||||
|
||||
GetEntryStore().SetIncomingReceiver(&continuingReceiver);
|
||||
GetEntryStore().SetOutgoingReceiver(&continuingReceiver);
|
||||
|
||||
|
||||
incomingStreamMonitor.start();
|
||||
writeManager.start();
|
||||
}
|
||||
@@ -35,6 +35,9 @@ NetworkTableServer::~NetworkTableServer(){
|
||||
|
||||
void NetworkTableServer::Close(){
|
||||
try{
|
||||
//Note: streamProvider must come before the incomingStreamMonitor so the that task can complete first for the thread to close
|
||||
// [9/1/2013 Terminator]
|
||||
streamProvider.close();
|
||||
incomingStreamMonitor.stop();
|
||||
writeManager.stop();
|
||||
connectionList.closeAll();
|
||||
|
||||
@@ -36,6 +36,9 @@ void ServerConnectionAdapter::badMessage(BadMessageException& e) {
|
||||
gotoState(new ServerConnectionState_Error(e));
|
||||
adapterListener.close(*this, true);
|
||||
m_IsAdapterListenerClosed=true;
|
||||
if (readThread) {
|
||||
readThread->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnectionAdapter::ioException(IOException& e) {
|
||||
@@ -46,6 +49,9 @@ void ServerConnectionAdapter::ioException(IOException& e) {
|
||||
else
|
||||
gotoState(new ServerConnectionState_Error(e));
|
||||
adapterListener.close(*this, false);
|
||||
if (readThread) {
|
||||
readThread->stop();
|
||||
}
|
||||
m_IsAdapterListenerClosed=true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "networktables2/server/ServerIncomingStreamMonitor.h"
|
||||
#include "networktables2/stream/IOStream.h"
|
||||
#include "networktables2/util/System.h"
|
||||
|
||||
ServerIncomingStreamMonitor::ServerIncomingStreamMonitor(IOStreamProvider& _streamProvider, ServerNetworkTableEntryStore& _entryStore,
|
||||
ServerIncomingConnectionListener& _incomingListener, ServerAdapterManager& _adapterListener, NetworkTableEntryTypeManager& _typeManager,
|
||||
@@ -69,6 +70,7 @@ void ServerIncomingStreamMonitor::run()
|
||||
ServerConnectionAdapter* connectionAdapter = new ServerConnectionAdapter(newStream, entryStore, entryStore, adapterListener, typeManager, threadManager);
|
||||
incomingListener.OnNewConnection(*connectionAdapter);
|
||||
}
|
||||
sleep_ms(100); //avoid busy wait
|
||||
}
|
||||
}
|
||||
catch (IOException& e)
|
||||
|
||||
@@ -18,9 +18,9 @@ ServerNetworkTableEntryStore::~ServerNetworkTableEntryStore()
|
||||
|
||||
bool ServerNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry)
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
NetworkTableEntry* entry = namedEntries[newEntry->name];
|
||||
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
newEntry->SetId(nextId++);
|
||||
@@ -44,13 +44,19 @@ bool ServerNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, Sequenc
|
||||
*/
|
||||
void ServerNetworkTableEntryStore::sendServerHello(NetworkTableConnection& connection)
|
||||
{
|
||||
NTSynchronized sync(LOCK);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
for (itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
std::vector<NetworkTableEntry *> entry_list;
|
||||
{
|
||||
NetworkTableEntry* entry = itr->second;
|
||||
connection.sendEntryAssignment(*entry);
|
||||
NTSynchronized sync(block_namedEntries);
|
||||
std::map<std::string, NetworkTableEntry*>::iterator itr;
|
||||
for (itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
|
||||
{
|
||||
NetworkTableEntry* entry = itr->second;
|
||||
entry_list.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i=0;i<entry_list.size();i++)
|
||||
connection.sendEntryAssignment(*(entry_list[i]));
|
||||
connection.sendServerHelloComplete();
|
||||
connection.flush();
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ni-libraries/libniimaqdx.so
Executable file → Normal file
BIN
ni-libraries/libniimaqdx.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libniimaqdx.so.14
Normal file
2
ni-libraries/libniimaqdx.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libniimaqdx.so.14.0 )
|
||||
2
ni-libraries/libniimaqdx.so.14.0
Normal file
2
ni-libraries/libniimaqdx.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libniimaqdx.so.14.0.0 )
|
||||
BIN
ni-libraries/libniimaqdx.so.14.0.0
Executable file
BIN
ni-libraries/libniimaqdx.so.14.0.0
Executable file
Binary file not shown.
BIN
ni-libraries/libnivision.so
Executable file → Normal file
BIN
ni-libraries/libnivision.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libnivision.so.14
Normal file
2
ni-libraries/libnivision.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivision.so.14.0 )
|
||||
2
ni-libraries/libnivision.so.14.0
Normal file
2
ni-libraries/libnivision.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivision.so.14.0.0 )
|
||||
BIN
ni-libraries/libnivision.so.14.0.0
Executable file
BIN
ni-libraries/libnivision.so.14.0.0
Executable file
Binary file not shown.
BIN
ni-libraries/libnivissvc.so
Executable file → Normal file
BIN
ni-libraries/libnivissvc.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libnivissvc.so.14
Normal file
2
ni-libraries/libnivissvc.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivissvc.so.14.0 )
|
||||
2
ni-libraries/libnivissvc.so.14.0
Normal file
2
ni-libraries/libnivissvc.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivissvc.so.14.0.0 )
|
||||
BIN
ni-libraries/libnivissvc.so.14.0.0
Executable file
BIN
ni-libraries/libnivissvc.so.14.0.0
Executable file
Binary file not shown.
@@ -5,6 +5,10 @@
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
/** @file
|
||||
* Contains global utility functions
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -91,11 +91,11 @@ void ErrorBase::SetImaqError(int success, const char *contextMessage, const char
|
||||
{
|
||||
// If there was an error
|
||||
if (success <= 0) {
|
||||
//TODO: ??? char err[256];
|
||||
// XXX: sprintf(err, "%s: %s", contextMessage, imaqGetErrorText(imaqGetLastError()));
|
||||
char err[256];
|
||||
sprintf(err, "%i: %s", success, contextMessage);
|
||||
|
||||
// Set the current error information for this object.
|
||||
// XXX: m_error.Set(imaqGetLastError(), err, filename, function, lineNumber, this);
|
||||
m_error.Set(success, err, filename, function, lineNumber, this);
|
||||
|
||||
// Update the global error if there is not one already set.
|
||||
Synchronized mutex(_globalErrorMutex);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "interfaces/Accelerometer.h"
|
||||
#include "I2C.h"
|
||||
#include "LiveWindow/LiveWindowSendable.h"
|
||||
|
||||
/**
|
||||
* ADXL345 Accelerometer on I2C.
|
||||
@@ -14,7 +15,7 @@
|
||||
* This class allows access to a Analog Devices ADXL345 3-axis accelerometer on an I2C bus.
|
||||
* This class assumes the default (not alternate) sensor address of 0x1D (7-bit address).
|
||||
*/
|
||||
class ADXL345_I2C : public Accelerometer, public I2C
|
||||
class ADXL345_I2C : public Accelerometer, public I2C, public LiveWindowSendable
|
||||
{
|
||||
protected:
|
||||
static const uint8_t kAddress = 0x1D;
|
||||
@@ -48,6 +49,15 @@ public:
|
||||
virtual double GetAcceleration(Axes axis);
|
||||
virtual AllAxes GetAccelerations();
|
||||
|
||||
virtual std::string GetSmartDashboardType();
|
||||
virtual void InitTable(ITable *subtable);
|
||||
virtual void UpdateTable();
|
||||
virtual ITable* GetTable();
|
||||
virtual void StartLiveWindowMode() {}
|
||||
virtual void StopLiveWindowMode() {}
|
||||
|
||||
protected:
|
||||
//I2C* m_i2c;
|
||||
private:
|
||||
ITable *m_table;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "interfaces/Accelerometer.h"
|
||||
#include "SensorBase.h"
|
||||
#include "SPI.h"
|
||||
#include "LiveWindow/LiveWindowSendable.h"
|
||||
|
||||
class DigitalInput;
|
||||
class DigitalOutput;
|
||||
@@ -18,7 +19,7 @@ class DigitalOutput;
|
||||
* This class allows access to an Analog Devices ADXL345 3-axis accelerometer via SPI.
|
||||
* This class assumes the sensor is wired in 4-wire SPI mode.
|
||||
*/
|
||||
class ADXL345_SPI : public Accelerometer, public SensorBase
|
||||
class ADXL345_SPI : public Accelerometer, public SensorBase, public LiveWindowSendable
|
||||
{
|
||||
protected:
|
||||
static const uint8_t kPowerCtlRegister = 0x2D;
|
||||
@@ -52,9 +53,18 @@ public:
|
||||
virtual double GetAcceleration(Axes axis);
|
||||
virtual AllAxes GetAccelerations();
|
||||
|
||||
virtual std::string GetSmartDashboardType();
|
||||
virtual void InitTable(ITable *subtable);
|
||||
virtual void UpdateTable();
|
||||
virtual ITable* GetTable();
|
||||
virtual void StartLiveWindowMode() {}
|
||||
virtual void StopLiveWindowMode() {}
|
||||
|
||||
protected:
|
||||
void Init(Range range);
|
||||
|
||||
SPI* m_spi;
|
||||
SPI::Port m_port;
|
||||
private:
|
||||
ITable *m_table;
|
||||
};
|
||||
|
||||
@@ -54,7 +54,10 @@ public:
|
||||
/** Only use switches for limits */
|
||||
kLimitMode_SwitchInputsOnly = 0,
|
||||
/** Use both switches and soft limits */
|
||||
kLimitMode_SoftPositionLimits = 1
|
||||
kLimitMode_SoftPositionLimits = 1,
|
||||
/* SRX extensions */
|
||||
/** Disable switches and disable soft limits */
|
||||
kLimitMode_SrxDisableSwitchInputs = 2,
|
||||
};
|
||||
|
||||
virtual float Get() = 0;
|
||||
|
||||
@@ -26,8 +26,15 @@ public:
|
||||
AnalogEncoder=3,
|
||||
EncRising=4,
|
||||
EncFalling=5
|
||||
};
|
||||
enum StatusFrameRate {
|
||||
StatusFrameRateGeneral=0,
|
||||
StatusFrameRateFeedback=1,
|
||||
StatusFrameRateQuadEncoder=2,
|
||||
StatusFrameRateAnalogTempVbat=3,
|
||||
};
|
||||
explicit CANTalon(int deviceNumber);
|
||||
explicit CANTalon(int deviceNumber,int controlPeriodMs);
|
||||
virtual ~CANTalon();
|
||||
|
||||
// PIDController interface
|
||||
@@ -51,6 +58,7 @@ public:
|
||||
virtual void SetI(double i) override;
|
||||
virtual void SetD(double d) override;
|
||||
void SetF(double f);
|
||||
void SetIzone(unsigned iz);
|
||||
virtual void SetPID(double p, double i, double d) override;
|
||||
void SetPID(double p, double i, double d, double f);
|
||||
virtual double GetP() override;
|
||||
@@ -66,6 +74,7 @@ public:
|
||||
virtual double GetSpeed() override;
|
||||
virtual int GetClosedLoopError();
|
||||
virtual int GetAnalogIn();
|
||||
virtual int GetAnalogInRaw();
|
||||
virtual int GetAnalogInVel();
|
||||
virtual int GetEncPosition();
|
||||
virtual int GetEncVel();
|
||||
@@ -91,15 +100,44 @@ public:
|
||||
virtual void ConfigLimitMode(LimitMode mode) override;
|
||||
virtual void ConfigForwardLimit(double forwardLimitPosition) override;
|
||||
virtual void ConfigReverseLimit(double reverseLimitPosition) override;
|
||||
/**
|
||||
* Change the fwd limit switch setting to normally open or closed.
|
||||
* Talon will disable momentarilly if the Talon's current setting
|
||||
* is dissimilar to the caller's requested setting.
|
||||
*
|
||||
* Since Talon saves setting to flash this should only affect
|
||||
* a given Talon initially during robot install.
|
||||
*
|
||||
* @param normallyOpen true for normally open. false for normally closed.
|
||||
*/
|
||||
void ConfigFwdLimitSwitchNormallyOpen(bool normallyOpen);
|
||||
/**
|
||||
* Change the rev limit switch setting to normally open or closed.
|
||||
* Talon will disable momentarilly if the Talon's current setting
|
||||
* is dissimilar to the caller's requested setting.
|
||||
*
|
||||
* Since Talon saves setting to flash this should only affect
|
||||
* a given Talon initially during robot install.
|
||||
*
|
||||
* @param normallyOpen true for normally open. false for normally closed.
|
||||
*/
|
||||
void ConfigRevLimitSwitchNormallyOpen(bool normallyOpen);
|
||||
virtual void ConfigMaxOutputVoltage(double voltage) override;
|
||||
virtual void ConfigFaultTime(float faultTime) override;
|
||||
virtual void SetControlMode(ControlMode mode);
|
||||
void SetFeedbackDevice(FeedbackDevice device);
|
||||
void SetStatusFrameRateMs(StatusFrameRate stateFrame, int periodMs);
|
||||
virtual ControlMode GetControlMode();
|
||||
void SetSensorDirection(bool reverseSensor);
|
||||
void SetCloseLoopRampRate(double rampRate);
|
||||
void SelectProfileSlot(int slotIdx);
|
||||
double GetIzone();
|
||||
int GetIzone();
|
||||
int GetIaccum();
|
||||
void ClearIaccum();
|
||||
int GetBrakeEnableDuringNeutral();
|
||||
|
||||
bool IsControlEnabled();
|
||||
double GetSetpoint();
|
||||
private:
|
||||
// Values for various modes as is sent in the CAN packets for the Talon.
|
||||
enum TalonControlMode {
|
||||
@@ -119,4 +157,15 @@ private:
|
||||
|
||||
bool m_controlEnabled;
|
||||
ControlMode m_controlMode;
|
||||
TalonControlMode m_sendMode;
|
||||
|
||||
double m_setPoint;
|
||||
static const unsigned int kDelayForSolicitedSignalsUs = 4000;
|
||||
/**
|
||||
* Fixup the sendMode so Set() serializes the correct demand value.
|
||||
* Also fills the modeSelecet in the control frame to disabled.
|
||||
* @param mode Control mode to ultimately enter once user calls Set().
|
||||
* @see Set()
|
||||
*/
|
||||
void ApplyControlMode(CANSpeedController::ControlMode mode);
|
||||
};
|
||||
|
||||
@@ -6,54 +6,76 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
#include "USBCamera.h"
|
||||
#include "ErrorBase.h"
|
||||
#include "nivision.h"
|
||||
#include "NIIMAQdx.h"
|
||||
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Class that runs a TCP server that serves an M-JPEG stream to the dashboard.
|
||||
*/
|
||||
class CameraServer : public ErrorBase {
|
||||
static constexpr uint16_t kPort = 1180;
|
||||
static constexpr uint8_t kMagicNumber[] = { 0x01, 0x00, 0x00, 0x00 };
|
||||
static constexpr uint32_t kSize640x480 = 0;
|
||||
static constexpr uint32_t kSize320x240 = 1;
|
||||
static constexpr uint32_t kSize160x120 = 2;
|
||||
static constexpr int32_t kHardwareCompression = -1;
|
||||
static constexpr char const *kDefaultCameraName = "cam1";
|
||||
private:
|
||||
static constexpr uint16_t kPort = 1180;
|
||||
static constexpr uint8_t kMagicNumber[] = { 0x01, 0x00, 0x00, 0x00 };
|
||||
static constexpr uint32_t kSize640x480 = 0;
|
||||
static constexpr uint32_t kSize320x240 = 1;
|
||||
static constexpr uint32_t kSize160x120 = 2;
|
||||
static constexpr int32_t kHardwareCompression = -1;
|
||||
static constexpr uint32_t kMaxImageSize = 200000;
|
||||
|
||||
public:
|
||||
static CameraServer *GetInstance();
|
||||
protected:
|
||||
CameraServer();
|
||||
|
||||
std::shared_ptr<USBCamera> m_camera;
|
||||
std::thread m_serverThread;
|
||||
std::thread m_captureThread;
|
||||
std::recursive_mutex m_imageMutex;
|
||||
std::condition_variable_any m_newImageVariable;
|
||||
std::vector<uint8_t*> m_dataPool;
|
||||
unsigned int m_quality;
|
||||
bool m_autoCaptureStarted;
|
||||
bool m_hwClient;
|
||||
std::tuple<uint8_t*, unsigned int, unsigned int, bool> m_imageData;
|
||||
|
||||
void SetImage(Image const *image);
|
||||
void Serve();
|
||||
void AutoCapture();
|
||||
void SetImageData(uint8_t* data, unsigned int size, unsigned int start = 0, bool imaqData = false);
|
||||
void FreeImageData(std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData);
|
||||
|
||||
void StartAutomaticCapture(char const *cameraName = kDefaultCameraName);
|
||||
struct Request {
|
||||
uint32_t fps;
|
||||
int32_t compression;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
void SetQuality(unsigned int quality);
|
||||
unsigned int GetQuality() const;
|
||||
static CameraServer* s_instance;
|
||||
|
||||
public:
|
||||
static CameraServer* GetInstance();
|
||||
void SetImage(Image const *image);
|
||||
|
||||
protected:
|
||||
CameraServer();
|
||||
void serve();
|
||||
void StartAutomaticCapture(char const *cameraName = USBCamera::kDefaultCameraName);
|
||||
|
||||
std::thread m_serverThread;
|
||||
std::recursive_mutex m_imageMutex;
|
||||
std::condition_variable_any m_newImageReady;
|
||||
std::vector<uint8_t> m_imageData;
|
||||
unsigned int m_quality;
|
||||
bool m_autoCaptureStarted;
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* You should call this method to just see a camera feed on the
|
||||
* dashboard without doing any vision processing on the roboRIO.
|
||||
* {@link #SetImage} should not be called after this is called.
|
||||
*
|
||||
* @param camera The camera interface (eg. USBCamera)
|
||||
*/
|
||||
void StartAutomaticCapture(std::shared_ptr<USBCamera> camera);
|
||||
|
||||
struct Request {
|
||||
uint32_t fps;
|
||||
int32_t compression;
|
||||
uint32_t size;
|
||||
};
|
||||
bool IsAutoCaptureStarted();
|
||||
|
||||
static CameraServer *s_instance;
|
||||
void SetQuality(unsigned int quality);
|
||||
unsigned int GetQuality();
|
||||
|
||||
void SetSize(unsigned int size);
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,14 @@ public:
|
||||
void SetClosedLoopControl(bool on);
|
||||
bool GetClosedLoopControl();
|
||||
|
||||
bool GetCompressorCurrentTooHighFault();
|
||||
bool GetCompressorCurrentTooHighStickyFault();
|
||||
bool GetCompressorShortedStickyFault();
|
||||
bool GetCompressorShortedFault();
|
||||
bool GetCompressorNotConnectedStickyFault();
|
||||
bool GetCompressorNotConnectedFault();
|
||||
void ClearAllPCMStickyFaults();
|
||||
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
bool GetDirection();
|
||||
void SetSamplesToAverage(int samplesToAverage);
|
||||
int GetSamplesToAverage();
|
||||
uint32_t GetIndex()
|
||||
uint32_t GetFPGAIndex()
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
virtual ~DoubleSolenoid();
|
||||
virtual void Set(Value value);
|
||||
virtual Value Get();
|
||||
bool IsFwdSolenoidBlackListed();
|
||||
bool IsRevSolenoidBlackListed();
|
||||
|
||||
void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew);
|
||||
void UpdateTable();
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
|
||||
float GetStickAxis(uint32_t stick, uint32_t axis);
|
||||
int GetStickPOV(uint32_t stick, uint32_t pov);
|
||||
uint32_t GetStickButtons(uint32_t stick);
|
||||
bool GetStickButton(uint32_t stick, uint8_t button);
|
||||
|
||||
int GetStickAxisCount(uint32_t stick);
|
||||
@@ -89,12 +90,16 @@ public:
|
||||
protected:
|
||||
DriverStation();
|
||||
|
||||
void GetData();
|
||||
private:
|
||||
static void InitTask(DriverStation *ds);
|
||||
static DriverStation *m_instance;
|
||||
void ReportJoystickUnpluggedError(std::string message);
|
||||
void Run();
|
||||
|
||||
HALJoystickAxes m_joystickAxes[kJoystickPorts];
|
||||
HALJoystickPOVs m_joystickPOVs[kJoystickPorts];
|
||||
HALJoystickButtons m_joystickButtons[kJoystickPorts];
|
||||
Task m_task;
|
||||
SEMAPHORE_ID m_newControlData;
|
||||
MULTIWAIT_ID m_packetDataAvailableMultiWait;
|
||||
|
||||
@@ -29,6 +29,7 @@ class DigitalSource;
|
||||
class Encoder : public SensorBase, public CounterBase, public PIDSource, public LiveWindowSendable
|
||||
{
|
||||
public:
|
||||
enum IndexingType { kResetWhileHigh, kResetWhileLow, kResetOnFallingEdge, kResetOnRisingEdge };
|
||||
|
||||
Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection = false,
|
||||
EncodingType encodingType = k4X);
|
||||
@@ -41,6 +42,7 @@ public:
|
||||
// CounterBase interface
|
||||
int32_t Get();
|
||||
int32_t GetRaw();
|
||||
int32_t GetEncodingScale();
|
||||
void Reset();
|
||||
double GetPeriod();
|
||||
void SetMaxPeriod(double maxPeriod);
|
||||
@@ -56,6 +58,10 @@ public:
|
||||
void SetPIDSourceParameter(PIDSourceParameter pidSource);
|
||||
double PIDGet();
|
||||
|
||||
void SetIndexSource(uint32_t channel, IndexingType type = kResetOnRisingEdge);
|
||||
void SetIndexSource(DigitalSource *source, IndexingType type = kResetOnRisingEdge);
|
||||
void SetIndexSource(DigitalSource &source, IndexingType type = kResetOnRisingEdge);
|
||||
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
@@ -63,6 +69,11 @@ public:
|
||||
void InitTable(ITable *subTable);
|
||||
ITable * GetTable();
|
||||
|
||||
int32_t GetFPGAIndex()
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitEncoder(bool _reverseDirection, EncodingType encodingType);
|
||||
double DecodingScaleFactor();
|
||||
@@ -72,9 +83,11 @@ private:
|
||||
bool m_allocatedASource; // was the A source allocated locally?
|
||||
bool m_allocatedBSource; // was the B source allocated locally?
|
||||
void* m_encoder;
|
||||
int32_t m_index; // The encoder's FPGA index.
|
||||
double m_distancePerPulse; // distance of travel for each encoder tick
|
||||
Counter *m_counter; // Counter object for 1x and 2x encoding
|
||||
EncodingType m_encodingType; // Encoding type
|
||||
int32_t m_encodingScale; // 1x, 2x, or 4x, per the encodingType
|
||||
PIDSourceParameter m_pidSource; // Encoder parameter that sources a PID controller
|
||||
|
||||
ITable *m_table;
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
virtual void TestPeriodic();
|
||||
|
||||
protected:
|
||||
virtual void Prestart();
|
||||
|
||||
virtual ~IterativeRobot();
|
||||
IterativeRobot();
|
||||
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
* Class implements the PWM generation in the FPGA.
|
||||
*
|
||||
* The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped
|
||||
* to the hardware dependent values, in this case 0-255 for the FPGA.
|
||||
* to the hardware dependent values, in this case 0-2000 for the FPGA.
|
||||
* Changes are immediately sent to the FPGA, and the update occurs at the next
|
||||
* FPGA cycle. There is no delay.
|
||||
*
|
||||
* As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-255 values as follows:
|
||||
* - 255 = full "forward"
|
||||
* - 254 to 129 = linear scaling from "full forward" to "center"
|
||||
* - 128 = center value
|
||||
* - 127 to 2 = linear scaling from "center" to "full reverse"
|
||||
* - 1 = full "reverse"
|
||||
* As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows:
|
||||
* - 2000 = maximum pulse width
|
||||
* - 1999 to 1001 = linear scaling from "full forward" to "center"
|
||||
* - 1000 = center value
|
||||
* - 999 to 2 = linear scaling from "center" to "full reverse"
|
||||
* - 1 = minimum pulse width (currently .5ms)
|
||||
* - 0 = disabled (i.e. PWM output is held low)
|
||||
*/
|
||||
class PWM : public SensorBase, public ITableListener, public LiveWindowSendable
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
#define __WPILIB_POWER_DISTRIBUTION_PANEL_H__
|
||||
|
||||
#include "SensorBase.h"
|
||||
#include "LiveWindow/LiveWindowSendable.h"
|
||||
|
||||
/**
|
||||
* Class for getting voltage, current, and temperature from the CAN PDP
|
||||
* Class for getting voltage, current, temperature, power and energy from the CAN PDP.
|
||||
* The PDP must be at CAN Address 0.
|
||||
* @author Thomas Clark
|
||||
*/
|
||||
class PowerDistributionPanel : public SensorBase {
|
||||
class PowerDistributionPanel : public SensorBase, public LiveWindowSendable {
|
||||
public:
|
||||
PowerDistributionPanel();
|
||||
|
||||
@@ -26,6 +28,16 @@ class PowerDistributionPanel : public SensorBase {
|
||||
double GetTotalEnergy();
|
||||
void ResetTotalEnergy();
|
||||
void ClearStickyFaults();
|
||||
|
||||
void UpdateTable();
|
||||
void StartLiveWindowMode();
|
||||
void StopLiveWindowMode();
|
||||
std::string GetSmartDashboardType();
|
||||
void InitTable(ITable *subTable);
|
||||
ITable * GetTable();
|
||||
|
||||
private:
|
||||
ITable *m_table;
|
||||
};
|
||||
|
||||
#endif /* __WPILIB_POWER_DISTRIBUTION_PANEL_H__ */
|
||||
|
||||
@@ -14,9 +14,9 @@ class DriverStation;
|
||||
int main() \
|
||||
{ \
|
||||
if (!HALInitialize()){std::cerr<<"FATAL ERROR: HAL could not be initialized"<<std::endl;return -1;} \
|
||||
HALNetworkCommunicationObserveUserProgramStarting(); \
|
||||
HALReport(HALUsageReporting::kResourceType_Language, HALUsageReporting::kLanguage_CPlusPlus); \
|
||||
(new _ClassName_())->StartCompetition(); \
|
||||
_ClassName_ *robot = new _ClassName_(); \
|
||||
RobotBase::robotSetup(robot); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
@@ -44,11 +44,15 @@ public:
|
||||
static void startRobotTask(FUNCPTR factory);
|
||||
static void robotTask(FUNCPTR factory, Task *task);
|
||||
virtual void StartCompetition() = 0;
|
||||
|
||||
static void robotSetup(RobotBase *robot);
|
||||
|
||||
protected:
|
||||
virtual ~RobotBase();
|
||||
RobotBase();
|
||||
|
||||
virtual void Prestart();
|
||||
|
||||
Task *m_task;
|
||||
DriverStation *m_ds;
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ class GenericHID;
|
||||
|
||||
/**
|
||||
* Utility class for handling Robot drive based on a definition of the motor configuration.
|
||||
* The robot drive class handles basic driving for a robot. Currently, 2 and 4 motor standard
|
||||
* drive trains are supported. In the future other drive types like swerve and meccanum might
|
||||
* be implemented. Motor channel numbers are passed supplied on creation of the class. Those are
|
||||
* used for either the Drive function (intended for hand created drive code, such as autonomous)
|
||||
* or with the Tank/Arcade functions intended to be used for Operator Control driving.
|
||||
* The robot drive class handles basic driving for a robot. Currently, 2 and 4 motor tank and
|
||||
* mecanum drive trains are supported. In the future other drive types like swerve might be
|
||||
* implemented. Motor channel numbers are passed supplied on creation of the class. Those
|
||||
* are used for either the Drive function (intended for hand created drive code, such as
|
||||
* autonomous) or with the Tank/Arcade functions intended to be used for Operator Control
|
||||
* driving.
|
||||
*/
|
||||
class RobotDrive : public MotorSafety, public ErrorBase
|
||||
{
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
void SetInvertedMotor(MotorType motor, bool isInverted);
|
||||
void SetSensitivity(float sensitivity);
|
||||
void SetMaxOutput(double maxOutput);
|
||||
void SetCANJaguarSyncGroup(uint8_t syncGroup);
|
||||
|
||||
void SetExpiration(float timeout);
|
||||
float GetExpiration();
|
||||
@@ -92,6 +94,7 @@ protected:
|
||||
SpeedController *m_rearLeftMotor;
|
||||
SpeedController *m_rearRightMotor;
|
||||
MotorSafetyHelper *m_safetyHelper;
|
||||
uint8_t m_syncGroup;
|
||||
|
||||
private:
|
||||
int32_t GetNumMotors()
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
enum StopBits {kStopBits_One=10, kStopBits_OnePointFive=15, kStopBits_Two=20};
|
||||
enum FlowControl {kFlowControl_None=0, kFlowControl_XonXoff=1, kFlowControl_RtsCts=2, kFlowControl_DtrDsr=4};
|
||||
enum WriteBufferMode {kFlushOnAccess=1, kFlushWhenFull=2};
|
||||
enum Port {kOnboard=0, kMXP=1};
|
||||
enum Port {kOnboard=0, kMXP=1, kUSB=2};
|
||||
|
||||
SerialPort(uint32_t baudRate, Port port = kOnboard, uint8_t dataBits = 8, Parity parity = kParity_None,
|
||||
StopBits stopBits = kStopBits_One);
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
virtual ~Solenoid();
|
||||
virtual void Set(bool on);
|
||||
virtual bool Get();
|
||||
bool IsBlackListed();
|
||||
|
||||
void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew);
|
||||
void UpdateTable();
|
||||
|
||||
@@ -20,6 +20,10 @@ public:
|
||||
virtual ~SolenoidBase();
|
||||
uint8_t GetAll();
|
||||
|
||||
uint8_t GetPCMSolenoidBlackList();
|
||||
bool GetPCMSolenoidVoltageStickyFault();
|
||||
bool GetPCMSolenoidVoltageFault();
|
||||
void ClearAllPCMStickyFaults();
|
||||
protected:
|
||||
explicit SolenoidBase(uint8_t pcmID);
|
||||
void Set(uint8_t value, uint8_t mask);
|
||||
|
||||
@@ -5,14 +5,25 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
#include "Talon.h"
|
||||
#include "SafePWM.h"
|
||||
#include "SpeedController.h"
|
||||
#include "PIDOutput.h"
|
||||
|
||||
/**
|
||||
* Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM control
|
||||
* @see CANTalon for CAN control
|
||||
*/
|
||||
class TalonSRX: public Talon {
|
||||
class TalonSRX : public SafePWM, public SpeedController
|
||||
{
|
||||
public:
|
||||
explicit TalonSRX(uint32_t channel);
|
||||
virtual ~TalonSRX();
|
||||
virtual void Set(float value, uint8_t syncGroup = 0);
|
||||
virtual float Get();
|
||||
virtual void Disable();
|
||||
|
||||
virtual void PIDWrite(float output);
|
||||
|
||||
private:
|
||||
void InitTalonSRX();
|
||||
};
|
||||
|
||||
111
wpilibc/wpilibC++Devices/include/USBCamera.h
Normal file
111
wpilibc/wpilibC++Devices/include/USBCamera.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
#include "ErrorBase.h"
|
||||
#include "nivision.h"
|
||||
#include "NIIMAQdx.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
typedef enum whiteBalance_enum {
|
||||
kFixedIndoor = 3000,
|
||||
kFixedOutdoor1 = 4000,
|
||||
kFixedOutdoor2 = 5000,
|
||||
kFixedFluorescent1 = 5100,
|
||||
kFixedFlourescent2 = 5200
|
||||
} whiteBalance;
|
||||
|
||||
class USBCamera : public ErrorBase {
|
||||
private:
|
||||
static constexpr char const *ATTR_WB_MODE = "CameraAttributes::WhiteBalance::Mode";
|
||||
static constexpr char const *ATTR_WB_VALUE = "CameraAttributes::WhiteBalance::Value";
|
||||
static constexpr char const *ATTR_EX_MODE = "CameraAttributes::Exposure::Mode";
|
||||
static constexpr char const *ATTR_EX_VALUE = "CameraAttributes::Exposure::Value";
|
||||
static constexpr char const *ATTR_BR_MODE = "CameraAttributes::Brightness::Mode";
|
||||
static constexpr char const *ATTR_BR_VALUE = "CameraAttributes::Brightness::Value";
|
||||
|
||||
protected:
|
||||
IMAQdxSession m_id;
|
||||
std::string m_name;
|
||||
bool m_useJpeg;
|
||||
bool m_active;
|
||||
bool m_open;
|
||||
|
||||
std::recursive_mutex m_mutex;
|
||||
|
||||
unsigned int m_width;
|
||||
unsigned int m_height;
|
||||
double m_fps;
|
||||
std::string m_whiteBalance;
|
||||
unsigned int m_whiteBalanceValue;
|
||||
bool m_whiteBalanceValuePresent;
|
||||
std::string m_exposure;
|
||||
unsigned int m_exposureValue;
|
||||
bool m_exposureValuePresent;
|
||||
unsigned int m_brightness;
|
||||
bool m_needSettingsUpdate;
|
||||
|
||||
unsigned int GetJpegSize(void* buffer, unsigned int buffSize);
|
||||
|
||||
public:
|
||||
static constexpr char const *kDefaultCameraName = "cam0";
|
||||
|
||||
USBCamera(std::string name, bool useJpeg);
|
||||
|
||||
void OpenCamera();
|
||||
void CloseCamera();
|
||||
void StartCapture();
|
||||
void StopCapture();
|
||||
void SetFPS(double fps);
|
||||
void SetSize(unsigned int width, unsigned int height);
|
||||
|
||||
void UpdateSettings();
|
||||
/**
|
||||
* Set the brightness, as a percentage (0-100).
|
||||
*/
|
||||
void SetBrightness(unsigned int brightness);
|
||||
|
||||
/**
|
||||
* Get the brightness, as a percentage (0-100).
|
||||
*/
|
||||
unsigned int GetBrightness();
|
||||
|
||||
/**
|
||||
* Set the white balance to auto
|
||||
*/
|
||||
void SetWhiteBalanceAuto();
|
||||
|
||||
/**
|
||||
* Set the white balance to hold current
|
||||
*/
|
||||
void SetWhiteBalanceHoldCurrent();
|
||||
|
||||
/**
|
||||
* Set the white balance to manual, with specified color temperature
|
||||
*/
|
||||
void SetWhiteBalanceManual(unsigned int wbValue);
|
||||
|
||||
/**
|
||||
* Set the exposure to auto exposure
|
||||
*/
|
||||
void SetExposureAuto();
|
||||
|
||||
/**
|
||||
* Set the exposure to hold current
|
||||
*/
|
||||
void SetExposureHoldCurrent();
|
||||
|
||||
/**
|
||||
* Set the exposure to manual, with a given percentage (0-100)
|
||||
*/
|
||||
void SetExposureManual(unsigned int expValue);
|
||||
|
||||
void GetImage(Image* image);
|
||||
unsigned int GetImageData(void* buffer, unsigned int bufferSize);
|
||||
};
|
||||
@@ -5,13 +5,24 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
#include "Talon.h"
|
||||
#include "SafePWM.h"
|
||||
#include "SpeedController.h"
|
||||
#include "PIDOutput.h"
|
||||
|
||||
/**
|
||||
* Vex Robotics Victor SP Speed Controller
|
||||
*/
|
||||
class VictorSP: public Talon {
|
||||
class VictorSP : public SafePWM, public SpeedController
|
||||
{
|
||||
public:
|
||||
explicit VictorSP(uint32_t channel);
|
||||
virtual ~VictorSP();
|
||||
virtual void Set(float value, uint8_t syncGroup = 0);
|
||||
virtual float Get();
|
||||
virtual void Disable();
|
||||
|
||||
virtual void PIDWrite(float output);
|
||||
|
||||
private:
|
||||
void InitVictorSP();
|
||||
};
|
||||
|
||||
127
wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
Normal file
127
wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "ErrorBase.h"
|
||||
#include "Vision/ColorImage.h"
|
||||
#include "Vision/HSLImage.h"
|
||||
#include "nivision.h"
|
||||
|
||||
/**
|
||||
* Axis M1011 network camera
|
||||
*/
|
||||
class AxisCamera: public ErrorBase
|
||||
{
|
||||
public:
|
||||
enum WhiteBalance
|
||||
{
|
||||
kWhiteBalance_Automatic,
|
||||
kWhiteBalance_Hold,
|
||||
kWhiteBalance_FixedOutdoor1,
|
||||
kWhiteBalance_FixedOutdoor2,
|
||||
kWhiteBalance_FixedIndoor,
|
||||
kWhiteBalance_FixedFluorescent1,
|
||||
kWhiteBalance_FixedFluorescent2
|
||||
};
|
||||
|
||||
enum ExposureControl
|
||||
{
|
||||
kExposureControl_Automatic,
|
||||
kExposureControl_Hold,
|
||||
kExposureControl_FlickerFree50Hz,
|
||||
kExposureControl_FlickerFree60Hz
|
||||
};
|
||||
|
||||
enum Resolution
|
||||
{
|
||||
kResolution_640x480,
|
||||
kResolution_480x360,
|
||||
kResolution_320x240,
|
||||
kResolution_240x180,
|
||||
kResolution_176x144,
|
||||
kResolution_160x120,
|
||||
};
|
||||
|
||||
enum Rotation
|
||||
{
|
||||
kRotation_0, kRotation_180
|
||||
};
|
||||
|
||||
explicit AxisCamera(std::string const& cameraHost);
|
||||
virtual ~AxisCamera();
|
||||
|
||||
bool IsFreshImage() const;
|
||||
|
||||
int GetImage(Image *image);
|
||||
int GetImage(ColorImage *image);
|
||||
HSLImage *GetImage();
|
||||
int CopyJPEG(char **destImage, unsigned int &destImageSize, unsigned int &destImageBufferSize);
|
||||
|
||||
void WriteBrightness(int brightness);
|
||||
int GetBrightness();
|
||||
|
||||
void WriteWhiteBalance(WhiteBalance whiteBalance);
|
||||
WhiteBalance GetWhiteBalance();
|
||||
|
||||
void WriteColorLevel(int colorLevel);
|
||||
int GetColorLevel();
|
||||
|
||||
void WriteExposureControl(ExposureControl exposureControl);
|
||||
ExposureControl GetExposureControl();
|
||||
|
||||
void WriteExposurePriority(int exposurePriority);
|
||||
int GetExposurePriority();
|
||||
|
||||
void WriteMaxFPS(int maxFPS);
|
||||
int GetMaxFPS();
|
||||
|
||||
void WriteResolution(Resolution resolution);
|
||||
Resolution GetResolution();
|
||||
|
||||
void WriteCompression(int compression);
|
||||
int GetCompression();
|
||||
|
||||
void WriteRotation(Rotation rotation);
|
||||
Rotation GetRotation();
|
||||
|
||||
private:
|
||||
std::thread m_captureThread;
|
||||
std::string m_cameraHost;
|
||||
int m_cameraSocket;
|
||||
std::mutex m_captureMutex;
|
||||
|
||||
std::mutex m_imageDataMutex;
|
||||
std::vector<uint8_t> m_imageData;
|
||||
bool m_freshImage;
|
||||
|
||||
int m_brightness;
|
||||
WhiteBalance m_whiteBalance;
|
||||
int m_colorLevel;
|
||||
ExposureControl m_exposureControl;
|
||||
int m_exposurePriority;
|
||||
int m_maxFPS;
|
||||
Resolution m_resolution;
|
||||
int m_compression;
|
||||
Rotation m_rotation;
|
||||
bool m_parametersDirty;
|
||||
bool m_streamDirty;
|
||||
std::mutex m_parametersMutex;
|
||||
|
||||
bool m_done;
|
||||
|
||||
void Capture();
|
||||
void ReadImagesFromCamera();
|
||||
bool WriteParameters();
|
||||
|
||||
int CreateCameraSocket(std::string const& requestString, bool setError);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AxisCamera);
|
||||
};
|
||||
57
wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
Normal file
57
wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Constants */
|
||||
#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
|
||||
#define LOG_INFO __FILE__,__FUNCTION__,__LINE__,INFO_TYPE
|
||||
#define LOG_ERROR __FILE__,__FUNCTION__,__LINE__,ERROR_TYPE
|
||||
#define LOG_CRITICAL __FILE__,__FUNCTION__,__LINE__,CRITICAL_TYPE
|
||||
#define LOG_FATAL __FILE__,__FUNCTION__,__LINE__,FATAL_TYPE
|
||||
#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
|
||||
|
||||
/* Enumerated Types */
|
||||
|
||||
/** debug levels */
|
||||
enum dprint_type {DEBUG_TYPE, INFO_TYPE, ERROR_TYPE, CRITICAL_TYPE, FATAL_TYPE};
|
||||
|
||||
/** debug output setting */
|
||||
typedef enum DebugOutputType_enum {
|
||||
DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY, DEBUG_SCREEN_AND_FILE
|
||||
}DebugOutputType;
|
||||
|
||||
/* Enumerated Types */
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
/* debug */
|
||||
void SetDebugFlag ( DebugOutputType flag );
|
||||
void dprintf ( const char * tempString, ... ); /* Variable argument list */
|
||||
|
||||
/* set FRC ranges for drive */
|
||||
double RangeToNormalized(double pixel, int range);
|
||||
/* change normalized value to any range - used for servo */
|
||||
float NormalizeToRange(float normalizedValue, float minRange, float maxRange);
|
||||
float NormalizeToRange(float normalizedValue);
|
||||
|
||||
/* system utilities */
|
||||
void ShowActivity (char *fmt, ...);
|
||||
double ElapsedTime (double startTime);
|
||||
|
||||
/* servo panning utilities */
|
||||
class Servo;
|
||||
double SinPosition (double *period, double sinStart);
|
||||
void panInit();
|
||||
void panInit(double period);
|
||||
void panForTarget(Servo *panServo);
|
||||
void panForTarget(Servo *panServo, double sinStart);
|
||||
|
||||
/* config file read utilities */
|
||||
int processFile(char *inputFile, char *outputString, int lineNumber);
|
||||
int emptyString(char *string);
|
||||
void stripString(char *string);
|
||||
|
||||
39
wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
Normal file
39
wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MonoImage.h"
|
||||
/**
|
||||
* Included for ParticleAnalysisReport definition
|
||||
* TODO: Eliminate this dependency!
|
||||
*/
|
||||
#include "Vision/VisionAPI.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
class BinaryImage : public MonoImage
|
||||
{
|
||||
public:
|
||||
BinaryImage();
|
||||
virtual ~BinaryImage();
|
||||
int GetNumberParticles();
|
||||
ParticleAnalysisReport GetParticleAnalysisReport(int particleNumber);
|
||||
void GetParticleAnalysisReport(int particleNumber, ParticleAnalysisReport *par);
|
||||
std::vector<ParticleAnalysisReport>* GetOrderedParticleAnalysisReports();
|
||||
BinaryImage *RemoveSmallObjects(bool connectivity8, int erosions);
|
||||
BinaryImage *RemoveLargeObjects(bool connectivity8, int erosions);
|
||||
BinaryImage *ConvexHull(bool connectivity8);
|
||||
BinaryImage *ParticleFilter(ParticleFilterCriteria2 *criteria, int criteriaCount);
|
||||
virtual void Write(const char *fileName);
|
||||
private:
|
||||
bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, int *result);
|
||||
bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, double *result);
|
||||
static double NormalizeFromRange(double position, int range);
|
||||
static bool CompareParticleSizes(ParticleAnalysisReport particle1, ParticleAnalysisReport particle2);
|
||||
};
|
||||
|
||||
65
wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
Normal file
65
wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImageBase.h"
|
||||
#include "BinaryImage.h"
|
||||
#include "Threshold.h"
|
||||
|
||||
class ColorImage : public ImageBase
|
||||
{
|
||||
public:
|
||||
ColorImage(ImageType type);
|
||||
virtual ~ColorImage();
|
||||
BinaryImage *ThresholdRGB(int redLow, int redHigh, int greenLow, int greenHigh, int blueLow, int blueHigh);
|
||||
BinaryImage *ThresholdHSL(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int luminenceLow, int luminenceHigh);
|
||||
BinaryImage *ThresholdHSV(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int valueHigh, int valueLow);
|
||||
BinaryImage *ThresholdHSI(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int intensityLow, int intensityHigh);
|
||||
BinaryImage *ThresholdRGB(Threshold &threshold);
|
||||
BinaryImage *ThresholdHSL(Threshold &threshold);
|
||||
BinaryImage *ThresholdHSV(Threshold &threshold);
|
||||
BinaryImage *ThresholdHSI(Threshold &threshold);
|
||||
MonoImage *GetRedPlane();
|
||||
MonoImage *GetGreenPlane();
|
||||
MonoImage *GetBluePlane();
|
||||
MonoImage *GetHSLHuePlane();
|
||||
MonoImage *GetHSVHuePlane();
|
||||
MonoImage *GetHSIHuePlane();
|
||||
MonoImage *GetHSLSaturationPlane();
|
||||
MonoImage *GetHSVSaturationPlane();
|
||||
MonoImage *GetHSISaturationPlane();
|
||||
MonoImage *GetLuminancePlane();
|
||||
MonoImage *GetValuePlane();
|
||||
MonoImage *GetIntensityPlane();
|
||||
void ReplaceRedPlane(MonoImage *plane);
|
||||
void ReplaceGreenPlane(MonoImage *plane);
|
||||
void ReplaceBluePlane(MonoImage *plane);
|
||||
void ReplaceHSLHuePlane(MonoImage *plane);
|
||||
void ReplaceHSVHuePlane(MonoImage *plane);
|
||||
void ReplaceHSIHuePlane(MonoImage *plane);
|
||||
void ReplaceHSLSaturationPlane(MonoImage *plane);
|
||||
void ReplaceHSVSaturationPlane(MonoImage *plane);
|
||||
void ReplaceHSISaturationPlane(MonoImage *plane);
|
||||
void ReplaceLuminancePlane(MonoImage *plane);
|
||||
void ReplaceValuePlane(MonoImage *plane);
|
||||
void ReplaceIntensityPlane(MonoImage *plane);
|
||||
void ColorEqualize();
|
||||
void LuminanceEqualize();
|
||||
|
||||
private:
|
||||
BinaryImage *ComputeThreshold(ColorMode colorMode, int low1, int high1, int low2, int high2, int low3, int high3);
|
||||
void Equalize(bool allPlanes);
|
||||
MonoImage * ExtractColorPlane(ColorMode mode, int planeNumber);
|
||||
MonoImage * ExtractFirstColorPlane(ColorMode mode);
|
||||
MonoImage * ExtractSecondColorPlane(ColorMode mode);
|
||||
MonoImage * ExtractThirdColorPlane(ColorMode mode);
|
||||
void ReplacePlane(ColorMode mode, MonoImage *plane, int planeNumber);
|
||||
void ReplaceFirstColorPlane(ColorMode mode, MonoImage *plane);
|
||||
void ReplaceSecondColorPlane(ColorMode mode, MonoImage *plane);
|
||||
void ReplaceThirdColorPlane(ColorMode mode, MonoImage *plane);
|
||||
};
|
||||
|
||||
30
wpilibc/wpilibC++Devices/include/Vision/FrcError.h
Normal file
30
wpilibc/wpilibC++Devices/include/Vision/FrcError.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Error Codes */
|
||||
#define ERR_VISION_GENERAL_ERROR 166000 //
|
||||
#define ERR_COLOR_NOT_FOUND 166100 // TrackAPI.cpp
|
||||
#define ERR_PARTICLE_TOO_SMALL 166101 // TrackAPI.cpp
|
||||
|
||||
#define ERR_CAMERA_FAILURE 166200 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_SOCKET_CREATE_FAILED 166201 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_CONNECT_FAILED 166202 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_STALE_IMAGE 166203 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_NOT_INITIALIZED 166204 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_NO_BUFFER_AVAILABLE 166205 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_HEADER_ERROR 166206 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_BLOCKING_TIMEOUT 166207 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_AUTHORIZATION_FAILED 166208 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_TASK_SPAWN_FAILED 166209 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE 166210 // AxisCamera.cpp
|
||||
#define ERR_CAMERA_COMMAND_FAILURE 166211 // AxisCamera.cpp
|
||||
|
||||
/* error handling functions */
|
||||
int GetLastVisionError();
|
||||
const char* GetVisionErrorText(int errorCode);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user