Particle Analysis Report Problems

I am trying to use camera tracking to track the reflective tape, but I am having problems with the frcParticleAnalysis function. I’m not sure how to use it. my code is:

#include “WPILib.h”

/**

  • This is a demo program showing the use of the RobotBase class.
  • The SimpleRobot class is the base of a robot application that will automatically call your
  • Autonomous and OperatorControl methods at the right time as controlled by the switches on
  • the driver station or the field controls.
    */
    class RobotDemo : public SimpleRobot
    {
    RobotDrive myRobot; // robot drive system
    Joystick stick; // only joystick
    Jaguar jag1;
    Jaguar jag2;
    Solenoid Sol1 ;
    Joystick stick2;
    HSLImage image;
    ParticleAnalysisReport par;
    BinaryImage image2;

public:
RobotDemo(void):
myRobot(1, 3, 2, 10), // front left, rear left, front right, rear right
stick(1), //these must be initialized in the same order
jag1(7),
jag2(8),
Sol1(8),
stick2(2)

{
	myRobot.SetExpiration(0.1);
}

/**
 * Drive left & right motors for 2 seconds then stop
 */
void Autonomous(void)
{
	myRobot.SetSafetyEnabled(false);
	AxisCamera &camera = AxisCamera::GetInstance();
	camera.WriteBrightness(0);
	Wait(3.0);
	myRobot.MecanumDrive_Cartesian(0.0, 0.0, 0.0, 0.0); 	// stop robot
	while (!IsOperatorControl())
	{
		camera.GetImage();			
		frcParticleAnalysis(image2, 5, par); /*the error says "cannot convert 'BinaryImage' to 'Image*' for argument '1' to 'int frcParticleAnalysis (Image*, int, ParticleAnalysisReport*)'*/
	}
}


/*
 * Runs the motors with single stick holonomic steering.
 * comment
 */
void OperatorControl(void)
{
	while (IsOperatorControl())
	{
		myRobot.SetSafetyEnabled(true);
		myRobot.MecanumDrive_Cartesian(stick.GetX(), stick.GetY(), stick.GetThrottle(), 0); // drive with arcade style (use right stick)
		if(stick.GetTrigger())
			jag2.SetSpeed(.8);
			jag1.SetSpeed(.5);
			Wait(.2);
			Sol1.Set(true);
		if(stick2.GetTrigger())
			jag1.SetSpeed(.5);
		else
			Sol1.Set(false);
			jag1.SetSpeed(0);
			jag2.SetSpeed(0);
		Wait(0.005);				// wait for a motor update time
	}
}

};

START_ROBOT_CLASS(RobotDemo);

You declare your variable image2 as a BinaryImage*, which is why the compiler is having trouble. It sees parameter 1’s type as “BinaryImage*”, but it is looking for Image*. Also, you don’t assign anything out of your call to camera.GetImage(). It should be something like image2 = camera.GetImage() [except that might not be the right type. Check out AxisCamera.h to find out what GetImage() returns].

I don’t have the code in front of me, but it is likely that what you need to do is do something like this:

ColorImage colorImage;
camera.getImage(&colorImage);
frcParticleAnalysis(&colorImage, 5, par);

However, we had success last night doing something different. We did something very similar to this. It is unlikely the code below or above will compile, it’s coming out of memory.
ColorImage img;
camera.getImage(&img);
BinaryImage* binImg = img.ThresholdRGB(192,256,192,156,192,256);
vector<ParticleAnalysisReport>* particles = binImg->GetOrderedReport(); // I don’t remember this function’s actual name
for(int x = 0;x < particles.size(); x++)
{
ParticleAnalysisReport& par = (*particles)[x];
// do something with par: look at par.center_mass_x, par.center_mass_y, etc.
}

Could you please post you actual code when you come in? It looks really good and I’d really like for my team to have camera tracking this year. Thanks so much!

this is my autonomous code as it stands now. can you guys help me figure out what to do/where to go from here.

void Autonomous(void)
{
	myRobot.SetSafetyEnabled(false);
	AxisCamera &camera = AxisCamera::GetInstance();
	camera.WriteBrightness(0);
	Wait(3.0);
	myRobot.MecanumDrive_Cartesian(0.0, 0.0, 0.0, 0.0); 	// stop robot
	while (!IsOperatorControl())
	{
/**		ColorImage &colorImagel;
		camera.GetImage(&colorImage);			
		frcParticleAnalysis(&colorImage, 5, par); **/
		HSLImage img;
		camera.GetImage(&img);
		BinaryImage* binImg = img.ThresholdRGB(192, 256, 192, 156, 192, 256);
		vector&lt;ParticleAnalysisReport&gt;* par = binImg-&gt;GetOrderedParticleAnalysisReports(); //warning: unused variable 'par'
		for(int x = 0; x &lt; 5/**par.x**/; x++)
		{
			ParticleAnalysisReport& par = (*particles)[x]; //error: 'particles' undeclared (first use this function)
			int x;
			par.center_mass_x = x;
			
			//do something with particles: par.center_mass_x & par.center_mass_y
		}
	}
}

thanks for the help


void Autonomous(void)
	{
		myRobot.SetSafetyEnabled(false);
		AxisCamera &camera = AxisCamera::GetInstance();
		camera.WriteBrightness(0);
		Wait(3.0);
		myRobot.MecanumDrive_Cartesian(0.0, 0.0, 0.0, 0.0); 	// stop robot
		while (!IsOperatorControl())
		{
			ColorImage img;
			camera.GetImage(&img);
			BinaryImage* binImg = img.ThresholdRGB(192, 256, 192, 156, 192, 256);
			vector<ParticleAnalysisReport>* particles = binImg->GetOrderedParticleAnalysisReports();
			for(UINT32 x = 0; x < particles->size(); x++)
			{
				ParticleAnalysisReport& par = (*particles)[x]; 
				
				if(par.center_mass_x_normalized > 0) { /* turn right */ }
				else if(par.center_mass_x_normalized < 0) { /* turn left */ }
			}
		}
	}

This should get you started. The robot centering will be kinda jerky once you put the turning calls into myRobot, but should mostly work.

If you’re focused JUST on autonomous however, don’t forget the physical approach: put an aiming device on the robot, and have your drive team place and aim the robot using that. That removes the need to use the camera at all, mostly. You wouldn’t get aiming in teleoperation mode, but if the goal is just autonomous, you can achieve it with just an on-robot aimer. It’s actually what we did last year.

awesome, well I have one more error to fix then I can just wait until electrical and mechanical have there stuff finished…

BinaryImage* binImg = img.TresholdRGB(192, 256,156, 192, 256); //error: request for member ‘ThresholdRGB’ in ‘image’, which is of non-class type ‘ColorImage*’

do I need to make img in to an RGB imag rather than just a Color Image?

You need to make it not a pointer.

The declaration for ColorImage should be:
ColorImage img;

From the compiler error, it sounds like you have
ColorImage* img;

That would be a pointer, which is why you can’t do
img.ThresholdRGB(…)

ok, when I do that i get

ColorImage img; //error: no matching function for call to ‘ColorImgae::ColorImage()’
camera.GetImage(&img);

if you want I’ll post the whole code as it stands now if you think it’ll help

Whoops, I forgot that ColorImage doesn’t have a default constructor.

I believe it is:
ColorImage img(IMAQ_RGB_IMAGE);

You’ll have to take a look at the constructor for ColorImage. Click on it, hit f3, and see what enumeration it requires. Hit f3 of the enumeration type, and it should take you to a list of options. You want the RGB one. Unfortunately, I’m once again at home, so I don’t have our code in front of me.

the constructor is IMAQ_IMAGE_RGB but it works now thanks!

As I’m always saying to our students - code compiling doesn’t mean the code works :slight_smile:

If you run into issues tracking with an RGB image, try using HSL instead.

  • Bryce

I know… I meant built right…

Now that I’ve finially had a chance to test the code, I’m getting errors when we try to run it.

<Code>-1074396120 ERROR: status = -1074396120 (0xBFF60428) Error counting particles: …in GetNumberParticles() in C:/WindRiver/workspace/WPILib/Vision/BinaryImage.cpp at line 29

<Code>-1074396120 ERROR: status = -1074396120 (0xBFF60428) ImaqThreshold error: …in ComputeThreshold() in C:/WindRiver/workspace/WPILib/Vision/ColorImage.cpp at line 37

<Code>-1074396120 ERROR: status = -1074396120 (0xBFF60428) Error counting particles: …in GetNumberParticles() in C:/WindRiver/workspace/WPILib/Vision/BinaryImage.cpp at line 29

what do they mean? and…

do you think using a different image type would help?

They’re all probably related to the same error. You’d have to post the exact code you’re using to call the ThresholdRGB function for me to have a clue.

Edit: the code I posted above that has the ThresholdRGB call has a problem. It should be
ThresholdRGB(192,256,192,256,192,256)

I put a 156 where 256 should have been.

I’m going to comment only on this single correction about the 156 versus 256…

Threshold takes 8-bit numbers for its thresholds. 256 is NOT an 8-bit number. 8-bit numbers range from (unsigned) 0-255 or (signed) -127 to 128. If you are passing 256 into this routine, you are likely passing a ZERO or an invalid number based upon the way Threshold processes it or passes it to the imaq functions.

255 is a MAX number …

bob

I looked through the documentation, but couldn’t find specifics. At least for the LV entry points the API is pretty forgiving and pretty intelligent. The underlying call to imaqColorThreshold takes in Range* datatypes for the three color specifications. A Range is two ints, which can encode 32 bit ints. I tested the LV entry points and they pin the ints into a reasonable range, so 256 is the same as 255. I’d expect the C functions to do the same. I don’t have the signature for the RGBThreshold wrapper in WPILib, but as long as it uses ints and not int8s, there shouldn’t be a problem, and even then, it would depend a bit on the ABI and implementation details.

Greg McKaskle

I can see your point, but the reality is that these images are 24-bit HSL or 24-bit RGB … 8 bits for each of H,S,L or 8-bits for each of R,G,B… so when you filter against them, you are filtering these exact 8-bit values in or out. So even though the API may take larger than 8-bit numbers, the max value you want to put in your filter is 255.

Thanks,
bob

I completely agree. It is confusing and possibly brittle to use a number that won’t fit into an int8, but as long as the RGBThreshold wrapper doesn’t cause it to be cast, the underlying IMAQ functions appear not to treat 256 as 0, but instead treat 256 as 255. In other words, they pin the input number into range rather than cast it.

Again, I’m basing that on the behavior of the LV entry points, and the C version could be different.

Also, the IMAQ analysis functions also support int16 monochrome, float monochrome, and even RGB64 images, so the pinning is useful for those cases as well.

Greg McKaskle

Hi, why do you use a for loop in your function? We have been trying to get the particle analysis working so we can use the FindColor function of TrackAPI.