.NET 程序员指南#
如果您是 pylon 的新用户,Basler 建议您先阅读 pylon .NET 程序员指南以熟悉 pylon .NET API。
.NET 编程示例#
要打开包含 blaze 相机编程示例的文件夹,请按下 Win 键,转到 Basler 文件夹,然后选择 blaze Samples。此时将打开一个文件资源管理器窗口。.NET 编程示例位于 DotNet 文件夹中。
如何构建示例#
信息
在构建示例之前,请将包含示例的文件夹复制到您具有读写权限的任意位置。
示例中提供了 Visual Studio 解决方案和项目文件。您可以使用 Visual Studio 2017 或更高版本打开解决方案并构建示例。
如何构建 .NET 应用程序#
若要使用 C# 或 Visual Basic .NET 操作 Basler blaze 相机,必须将 Basler.Pylon 和 Basler.Pylon.Blaze 程序集添加到项目的程序集引用列表中。
要添加程序集:
- 在 Visual Studio 解决方案资源管理器中右键单击您的项目,然后选择 添加 > 项目引用。
此时将打开一个引用管理器窗口。 - 点击浏览按钮。
- 导航到 %Program Files%\Basler\pylon 8\Development\Assemblies\Basler.Pylon\x64 文件夹。
- 选择 Basler.Pylon.dll,然后点击添加按钮。
- 点击浏览按钮。
- 导航到 %Program Files%\Basler\pylon 8\Development\Assemblies\Basler.Pylon.Blaze\AnyCpu 文件夹。
- 选择 Basler.Pylon.Blaze.dll,然后点击添加按钮。
Basler.Pylon 程序集的处理器架构
无论您选择针对 32 位还是 64 位平台的程序集,都没有关系。由于这些程序集已安装在全局程序集缓存 (GAC) 中,因此 .NET 程序集加载器会自动选择与您的应用程序构建架构相匹配的程序集。
为避免在构建应用程序时出现关于潜在架构不匹配的警告,Basler 建议手动编辑 Visual Studio 项目文件以隐藏此类警告。操作步骤如下:
- 在“解决方案资源管理器”中,右键单击您的项目并选择 卸载项目。
- 再次右键单击该项目并选择 编辑项目。
该项目的 XML 文件将在编辑器窗口中打开。 -
在 XML 文件中,导航至
Basler.Pylon程序集被引用的位置,即搜索类似于以下内容的片段: -
在
ItemGroup元素上方,添加以下 XML 片段:<PropertyGroup> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup>添加元素后,您的项目文件应包含如下所示的片段:
<PropertyGroup> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup> <ItemGroup> .... <Reference Include="Basler.Pylon"> <HintPath>..\..\..\..\..\..\Program Files\Basler\pylon\Development\Assemblies\Basler.Pylon\x64\Basler.Pylon.dll</HintPath> </Reference> <Reference Include="Basler.Pylon.Blaze"> <HintPath>..\..\..\..\..\..\Program Files\Basler\pylon\Development\Assemblies\Basler.Pylon.Blaze\AnyCpu\Basler.Pylon.Blaze.dll</HintPath> </Reference> .... </ItemGroup> -
编辑完成后,右键单击项目并选择 重新加载项目来重新加载您的项目。
如果系统提示,请确认关闭项目文件并保存未保存的更改。
打开和访问 blaze 相机#
pylon .NET API 为所有相机类型提供了一个单一的 Camera 类。可以使用重载构造函数来选择特定的相机设备。
创建并打开 blaze 相机对象需要三个步骤:
-
为要访问的相机创建一个相机对象。
-
注册应用默认配置的 blaze 专用参数配置。有关 blaze 配置事件处理程序类的更多详细信息,请参阅预定义相机参数配置部分。
-
调用
Open()方法以建立与相机设备的连接。
以下示例说明了这三个步骤:
using Basler.Pylon;
using Basler.Pylon.Blaze;
// ...
var filter = new Dictionary<string, string> {
{ CameraInfoKey.DeviceType, DeviceType.BaslerGenTlBlazeDeviceClass }
};
using (var camera = new Camera(filter, CameraSelectionStrategy.FirstFound))
{
// Set up the camera for continuously grabbing point clouds.
camera.CameraOpened += BlazeConfigurations.AcquirePointCloudsContinuously;
// Open the connection to the camera device.
camera.Open();
// Use the camera.
// Close the connection.
camera.Close();
}
Imports Basler.Pylon
Imports Basler.Pylon.Blaze
' ...
Dim filter = New Dictionary(Of String, String) From {
{CameraInfoKey.DeviceType, DeviceType.BaslerGenTlBlazeDeviceClass}
}
Using camera = New Camera(filter, CameraSelectionStrategy.FirstFound)
' Set up the camera for continuously grabbing point clouds.
AddHandler camera.CameraOpened,
AddressOf BlazeConfigurations.AcquirePointCloudsContinuously
' Open the connection to the camera device.
camera.Open()
' Use the camera.
' Close the connection.
camera.Close()
End Using
为首个可用的 blaze 相机创建一个相机对象#
即: Camera 类提供了一个构造函数,允许您传递键值对列表,用于指定为您创建相机对象的相机设备的 Property。
要打开第一个可用的 blaze 相机,只需指定设备类型等于 blaze 设备类即可。
为特定的 blaze 相机创建一个相机对象#
如果您想打开特定的 blaze 相机(例如通过指定其序列号),除了将用于 blaze 相机的 pylon 设备类标识符传递给 CreateFirstDevice()之外,您还可以指定其他 Property。
以下代码片段说明了如何通过序列号打开 blaze 相机。
要通过用户自定义名称打开 blaze 相机,请使用以下代码行:
用户自定义名称
您可以使用以下参数为相机指定用户自定义名称: DeviceUserID 您可以在 blaze Viewer、pylon IP Configurator 或 pylon API 中执行此操作:
通过 IP 地址创建设备
目前,您无法通过指定 IP 地址来创建设备。
如果您想创建并打开具有特定 IP 地址的相机,必须首先枚举所有相机、遍历返回的列表,并在查询 IP 地址及其他网络相关信息之前打开每个相机。
如果未找到所需的 blaze 相机,请检查您是否使用了正确的属性值,并检查相机和网卡的 IP 配置设置。有关设置网络的更多信息,请参阅网络配置主题。
预定义的相机参数配置#
对于 blaze 相机,有 BlazeConfigurations 类。该类的方法可以添加到 CameraOpened 事件中。必须在通过调用 Open() 方法打开相机之前完成此操作。打开相机时,会自动调用配置方法并相应地更改相机配置。
示例:
即: BlazeConfigurations 提供以下配置方法:
AcquirePointCloudsContinuously
连续采集数据。深度数据表示为点云。AcquireDepthMapsContinuously
连续采集数据。深度数据表示为深度图。
有关 pylon 配置事件处理程序的更多信息,请参阅 pylon .NET 程序员指南中的预定义相机参数配置一节。
解释深度数据#
处理测量结果主题提供了有关存储在 Range 数据组件中的深度数据格式的信息。
连接到相机#
创建相机对象并注册配置事件处理程序后,通过调用 CBlazeInstantCamera::Open() 方法来建立与相机的连接。
连接将保持活动状态,直到调用 CBlazeInstantCamera::Close() 方法。如果未显式调用, Camera 类的析构函数会自动调用 Close() 。为了显式控制相机对象的生命周期,Basler 建议如上方代码片段所示,在 using 语句内部使用相机对象。
如果相机未定期收到来自相机的心跳请求,它将关闭连接。如果应用程序崩溃或被调试器中断,则会出现心跳请求丢失的情况。有关更多详细信息,请参阅调试应用程序和控制 GigE Vision 心跳一节。
访问参数#
有关如何使用 pylon .NET API 访问相机参数的详细信息,请参阅 pylon .NET 程序员指南中的参数访问一节。
pylon .NET API 提供参数列表作为访问相机参数的便捷方法,并在 Visual Studio 的 IntelliSense 辅助下完成。
对于 blaze 相机,有 PLBlaze 参数列表。
以下代码片段演示了如何使用以下工具设置和获取 Property: PLBlaze 参数列表。
// Set the operating mode of the camera. The choice you make here
// affects the working range of the camera, i.e., the Minimum Working
// Range and Maximum Working Range parameters.
var oldOperatingMode = camera.Parameters[PLBlaze.OperatingMode].GetValue();
camera.Parameters[PLBlaze.OperatingMode].SetValue(PLBlaze.OperatingMode.LongRange);
// Exposure time of the camera. If the operating mode is changed, the
// exposure time is set to the recommended default value.
camera.Parameters[PLBlaze.ExposureTime].SetValue(750); // us
// Enable and configure image filtering.
// The spatial noise filter uses the values of neighboring pixels to
// filter out noise in an image.
camera.Parameters[PLBlaze.SpatialFilter].SetValue(true);
// The temporal noise filter uses the values of the same pixel at
// different points in time to filter out noise in an image.
camera.Parameters[PLBlaze.TemporalFilter].SetValue(true);
camera.Parameters[PLBlaze.TemporalFilterStrength].SetValue(220);
// The outlier removal removes pixels that differ significantly from
// their local environment.
camera.Parameters[PLBlaze.OutlierRemoval].SetValue(true);
// Some properties have restrictions.
// We use API functions that automatically perform value corrections.
// Alternatively, you can use GetInc() / GetMin() / GetMax() to make sure you
// set a valid value.
camera.Parameters[PLBlaze.ConfidenceThreshold].SetValue(
321,
IntegerValueCorrection.Nearest
);
// Not all functions are available in older cameras.
// Therefore, we must use "Try" functions that only perform the action
// when parameters are writable. Otherwise, we would get an exception.
camera.Parameters[PLBlaze.MultiCameraChannel].TrySetValue(1);
Console.WriteLine("Operating Mode : {0}",
camera.Parameters[PLBlaze.OperatingMode].GetValue());
Console.WriteLine("Exposure Time : {0}",
camera.Parameters[PLBlaze.ExposureTime].GetValue());
Console.WriteLine("Spatial Filter : {0}",
camera.Parameters[PLBlaze.SpatialFilter].GetValue());
Console.WriteLine("Temporal Filter : {0}",
camera.Parameters[PLBlaze.TemporalFilter].GetValue());
Console.WriteLine("Temporal Filter Strength : {0}",
camera.Parameters[PLBlaze.TemporalFilterStrength].GetValue());
Console.WriteLine("Outlier Removal : {0}",
camera.Parameters[PLBlaze.OutlierRemoval].GetValue());
Console.WriteLine("Confidence Threshold : {0}",
camera.Parameters[PLBlaze.ConfidenceThreshold].GetValue());
if (camera.Parameters[PLBlaze.MultiCameraChannel].IsReadable)
{
Console.WriteLine("Multi-Camera Channel : {0}",
camera.Parameters[PLBlaze.MultiCameraChannel].GetValue());
}
// Restore the old operating mode.
camera.Parameters[PLBlaze.OperatingMode].SetValue(oldOperatingMode);
' Set the operating mode of the camera. The choice you make here
' affects the working range of the camera, i.e., the Minimum Working
' Range and Maximum Working Range parameters.
Dim oldOperatingMode = camera.Parameters(PLBlaze.OperatingMode).GetValue()
camera.Parameters(PLBlaze.OperatingMode).SetValue(PLBlaze.OperatingMode.LongRange)
' Exposure time of the camera. If the operating mode is changed, the
' exposure time is set to the recommended default value
camera.Parameters(PLBlaze.ExposureTime).SetValue(750) ' us
' Enable and configure image filtering.
' The spatial noise filter uses the values of neighboring pixels to
' filter out noise in an image.
camera.Parameters(PLBlaze.SpatialFilter).SetValue(True)
' The temporal noise filter uses the values of the same pixel at
' different points in time to filter out noise in an image.
camera.Parameters(PLBlaze.TemporalFilter).SetValue(True)
camera.Parameters(PLBlaze.TemporalFilterStrength).SetValue(220)
' The outlier removal removes pixels that differ significantly from
' their local environment
camera.Parameters(PLBlaze.OutlierRemoval).SetValue(True)
' Some properties have restrictions.
' We use API functions that automatically perform value corrections.
' Alternatively, you can use GetInc() / GetMin() / GetMax() to make sure you set
' a valid value.
camera.Parameters(PLBlaze.ConfidenceThreshold).SetValue(
321,
IntegerValueCorrection.Nearest
)
' Not all functions are available in older cameras.
' Therefore, we must use "Try" functions that only perform the action
' when parameters are writable. Otherwise, we would get an exception.
camera.Parameters(PLBlaze.MultiCameraChannel).TrySetValue(1)
Console.WriteLine("Operating Mode : {0}",
camera.Parameters(PLBlaze.OperatingMode).GetValue())
Console.WriteLine("Exposure Time : {0}",
camera.Parameters(PLBlaze.ExposureTime).GetValue())
Console.WriteLine("Spatial Filter : {0}",
camera.Parameters(PLBlaze.SpatialFilter).GetValue())
Console.WriteLine("Temporal Filter : {0}",
camera.Parameters(PLBlaze.TemporalFilter).GetValue())
Console.WriteLine("Temporal Filter Strength : {0}",
camera.Parameters(PLBlaze.TemporalFilterStrength).GetValue())
Console.WriteLine("Outlier Removal : {0}",
camera.Parameters(PLBlaze.OutlierRemoval).GetValue())
Console.WriteLine("Confidence Threshold : {0}",
camera.Parameters(PLBlaze.ConfidenceThreshold).GetValue())
If camera.Parameters(PLBlaze.MultiCameraChannel).IsReadable Then
Console.WriteLine("Multi-Camera Channel : {0}",
camera.Parameters(PLBlaze.MultiCameraChannel).GetValue())
End If
' Restore the old operating mode.
camera.Parameters(PLBlaze.OperatingMode).SetValue(oldOperatingMode)
采集数据#
请参考 Grabbing Images 章节(位于 pylon .NET Programmer's Guide 中),了解如何使用 pylon .NET API 获取数据。
pylon 将抓取到的图像表示为名为 GrabResults 的数据结构。由 2D 相机获取的 GrabResult 包含单个灰度图或彩色图。
由 blaze 相机抓取的 GrabResult 包含多个组件。默认情况下,每个 GrabResult 存储强度图、置信度图以及深度信息。
根据为 Range 组件设置的 Pixel Format,深度数据可以表示为 2D 深度图或 3D 点云。
抓取循环#
pylon 支持用于设置抓取循环的不同方法,并提供了用于处理 Memory 缓冲区的不同策略。
有关更多详细信息,请参考 Grabbing Images 章节(位于 pylon .NET Programmer's Guide 中)。
以下代码示例说明了一个典型的抓取循环:
// Open the connection to the camera device.
camera.Open();
// The MaxNumBuffer parameter can be used to control the amount of buffers
// allocated for grabbing. The default value of this parameter is 10.
camera.Parameters[PLCameraInstance.MaxNumBuffer].SetValue(3);
// Start grabbing.
camera.StreamGrabber.Start();
// Grab a number of images.
for (int i = 0; i < 10; ++i)
{
// Wait for an image and then retrieve it. A timeout of 1000 ms is used.
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
);
using (grabResult) // Make sure that the grab buffers are reused for
// grabbing as soon we run out of scope.
{
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
// Access the data.
// ....
}
else
{
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode, grabResult.ErrorDescription);
}
}
}
// Stop grabbing.
camera.StreamGrabber.Stop();
' Open the connection to the camera device.
camera.Open()
' The MaxNumBuffer parameter can be used to control the amount of buffers
' allocated for grabbing. The default value of this parameter is 10.
camera.Parameters(PLCameraInstance.MaxNumBuffer).SetValue(3)
' Start grabbing.
camera.StreamGrabber.Start()
' Grab a number of images.
For i As Integer = 0 To 10 - 1
' Wait for an image and then retrieve it. A timeout of 1000 ms is used.
Dim grabResult As IGrabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
)
Using grabResult
' Image grabbed successfully?
If grabResult.GrabSucceeded Then
' Access the data.
' ....
Else
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode,
grabResult.ErrorDescription)
End If
End Using
Next
' Stop grabbing.
camera.StreamGrabber.Stop()
访问组件#
要访问 GrabResult 的各个组件,您可以使用 IDataContainer 和 IDataComponent 类。一个容器可以容纳一个或多个组件。您可以使用该容器来查询组件数量并检索特定组件。容器中的每个组件都包含实际数据(例如深度值)以及元数据。
使用 IGrabResult::GetDataContainer() 方法来访问 GrabResult 的 IDataContainer.
有关 pylon 如何提供对包含多个组件的 GrabResults 的访问的更多信息,请参考 Multi-Component Grab Results 章节(位于 pylon .NET Programmer's Guide 中)。
示例:
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(
1000,
TimeoutHandling.ThrowException
);
using (grabResult)
{
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
// Access the components.
// The container and the components should always be used in combination
// with using statements. This ensures that the underlying grab buffers
// are reused for grabbing new data as soon as the variables go out
// of scope. Omitting the using statements and relying on the garbage
// collector instead will cause buffer underruns resulting in missed data.
using (var container = grabResult.Container)
using (var rangeComponent = container[0])
using (var intensityComponent = container[1])
using (var confidenceComponent = container[2])
{
var pointCloudData = rangeComponent.PixelData as float[];
var intensityData = intensityComponent.PixelData as UInt16[];
var confidenceData = confidenceComponent.PixelData as UInt16[];
// Access the center pixel.
var u = (uint)(rangeComponent.Width / 2);
var v = (uint)(rangeComponent.Height / 2);
var idx = v * rangeComponent.Width + u;
var intensity = intensityData[idx];
var confidence = confidenceData[idx];
// There is a triple (x,y,z) of floats per pixel.
var x = pointCloudData[3 * idx];
var y = pointCloudData[3 * idx + 1];
var z = pointCloudData[3 * idx + 2];
if (confidence != 0) // Alternatively: if (z != 0)
{
// Valid depth data
Console.WriteLine("x={0}, y={1}, z={2}, intensity={3}, confidence = {4}",
x, y, z, intensity, confidence);
}
else
{
Console.WriteLine("No depth data available for pixel ({0},{1}), intensity={2}",
u, v, intensity);
}
}
else
{
Console.WriteLine("Error: {0} {1}",
grabResult.ErrorCode, grabResult.ErrorDescription);
}
}
Dim grabResult As IGrabResult =
camera.StreamGrabber.RetrieveResult(5000, TimeoutHandling.ThrowException)
Using grabResult
' Image grabbed successfully?
If grabResult.GrabSucceeded Then
' Access the components.
' The container and the components should always be used in combination with
' using statements. This ensures that the underlying grab buffers are reused
' for grabbing new data as soon as the variables go out of scope.
' Omitting the using statements and relying on the garbage collector instead
' will cause buffer underruns resulting in missed data.
Using container = grabResult.Container,
rangeComponent = container(0),
intensityComponent = container(1),
confidenceComponent = container(2)
Dim pointCloudData = TryCast(rangeComponent.PixelData, Single())
Dim intensityData = TryCast(intensityComponent.PixelData, UInt16())
Dim confidenceData = TryCast(confidenceComponent.PixelData, UInt16())
' Access the center pixel.
Dim u = CUInt((rangeComponent.Width / 2))
Dim v = CUInt((rangeComponent.Height / 2))
Dim idx = v * rangeComponent.Width + u
Dim intensity = intensityData(idx)
Dim confidence = confidenceData(idx)
Dim x = pointCloudData(3 * idx)
Dim y = pointCloudData(3 * idx + 1)
Dim z = pointCloudData(3 * idx + 2)
If confidence <> 0 Then ' Alternatively: if (z != 0)
' Valid depth data
Console.WriteLine("x={0}, y={1}, z={2}, intensity={3}, confidence = {4}",
x, y, z, intensity, confidence)
Else
Console.WriteLine("No depth data available for pixel ({0},{1}), intensity={2}",
u, v, intensity)
End If
End Using
Else
Console.WriteLine("Error: {0} {1}", grabResult.ErrorCode,
grabResult.ErrorDescription)
End If
End Using
解释深度数据#
处理测量结果主题提供了有关存储在 Range 数据组件中的深度数据格式的信息。
调试应用程序并控制 GigE Vision Heartbeat#
使用 pylon 7.0 或更低版本时的重新连接问题
如果应用程序在正确关闭与相机的连接之前终止或崩溃,相机将保持连接打开状态,直到心跳超时过期。在心跳超时过期之前,相机将拒绝任何进一步的连接尝试。重新启动应用程序或启动请求访问相机的另一个应用程序时,您将收到一条错误消息,指出设备当前正在使用中。
这通常发生在使用调试器停止应用程序或应用程序意外终止时。要再次打开相机,您必须等待超时结束,或者暂时断开相机与网络的连接。
为了解决此问题,当应用程序在调试器的控制下运行时,您可以通过将名为 GEV_HEARTBEAT_TIMEOUT 的环境变量设置为所需的超时时间(以毫秒为单位)来覆盖心跳超时的自动调整。或者,您可以使用 pylon API 在应用程序中设置心跳超时,如 DeviceRemovalCSharp 和 DeviceRemovalVB 示例程序中所示。