文章目录
- openpnp - 接入西门子二手飞达
- 概述
- 笔记
- 编译生产用的飞达控制板工程
- 建立飞达控制板GCode驱动
- create actuators
- create actuators - GetID
- create actuators - PrePick
- create actuators - PostPick
- create actuators - AdvIgnoreErr
- create actuators - GetCount
- create actuators - ClearCount
- create actuators - GetPitch
- create actuators - TogglePitch
- create actuators - GetStatus
- 在飞达控制板的gcode驱动中添加G码
- 在openpnp中添加西门子电动飞达
- 为西门子飞达上面每一位子飞达都建立一个基准点封装
- 飞达的子飞达号码的判断
- 飞达ID的约定
- 为这个测试飞达的子飞达0添加一个openpnp飞达
- 先手工添加一个SlotSchultzFeeder
- 设置取料的位置
- 补充 - 轴的移动范围可以跨零点
- 补充 - 如果料盘摆歪了会导致送料错误
- 补充 - 飞达的物料在编带内的旋转角度和散料飞达不一样
- 子飞达1的添加
- 备注
- 备注
- END
openpnp - 接入西门子二手飞达
概述
SchultzFeeders官方直接给的例子, 让新手用户直接将作者给的配置贴到machine.xml中.
如果以后opepnp升级了, 配置文件格式变了, 这么硬改, 可能会有问题. 因为无法确定自己用的openpnp版本, 是否认得这些xml字段
有些UID字段, 让openpnp自动生成好一些, 万一重复了呢?
我的想法是将作者给的例子整理出来, 看明白了, 然后用openpnp界面填入, 这样等openpnp升级了(或者在不同版本的openpnp中接入二手西门子飞达), 也不会有任何问题.
官方文档给的操作步骤也可以优化, 要不小白有点晕.
最开始时(设备都没调试好, 处于最初的小白状态), 自己按照SchultzFeeders官方配置过西门子电动飞达, 配置的效果都不对, 也不知道哪里做错了.
现在接触了openpnp有一段时间了, 看着官方给的文档, 只要看个大概, 自己就能知道怎么活学活用.
笔记
如果在openpnp中改了内容, 在切换到其他UI之前, 一定要保存(当前页面右小角有应用按钮), 否则再回到此页面, 参数就丢了.
编译生产用的飞达控制板工程
将工程中的DEBUG宏去掉, 重新编译上传.
保证串口收发不会有多余的内容.
将飞达控制板连接到PC机USB串口.
建立飞达控制板GCode驱动
手工取得固件版本信息, 等于是执行了M115
现在一个空的飞达控制板驱动就建立好了
发现有时通讯超时, 将等待时间改长一些, 如果飞达控制板处于空闲状态, 可以很快回包. 如果飞达控制板正在重启或忙于其他任务, 也可以等待回包, 不至于让openpnp操作飞达控制板时报错. 将进给速度改慢点, 因为通讯速度没那么快. 看情况调整.
create actuators
作者给的例子一共有9个actuator, 逐个整理, 然后在openpnp中逐个手工添加动作.
actuator的名字, 将动作放在前面, Schultz放在后面, 防止手工操作动作时, 看不全名称, e.g. GetID_Schultz
每个添加的西门子飞达动作, 用到的驱动, 都选择上面自己建立的实际飞达控制板串口GCode驱动Schultz_Driver.
create actuators - GetID
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzGetID"
name="SchultzGetID"
value-type="Double"
value-type-confirmed="true"
default-on-double="0.0"
default-on-string=""
default-off-double="0.0"
default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
看到要建立的Actuator类型是ReferenceActuator
Actuator建立的位置是设备树根目录下的Actuator
值类型是Double, 默认值是0
coordinated-before-actuate=“false”
coordinated-after-actuate=“false”
coordinated-before-read=“false”
enabled-actuation=“LeaveAsIs”
homed-actuation=“LeaveAsIs”
disabled-actuation=“LeaveAsIs”
这6个和UI上都对的上
index = 0
将以上整理后看到的内容填入UI
create actuators - PrePick
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzPrePick" name="SchultzPrePick"
value-type="Double"
value-type-confirmed="true"
default-on-double="0.0"
default-on-string=""
default-off-double="0.0"
default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - PostPick
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzPostPick"
name="SchultzPostPick" // PostPick_Schultz
value-type="Double"
value-type-confirmed="true"
default-on-double="0.0"
default-on-string=""
default-off-double="0.0"
default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
现在按照官方资料加了3个动作, 可以看到这些动作只有名称不一样.
create actuators - AdvIgnoreErr
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzAdvIgnorErr"
name="SchultzAdvIgnoreErr" // AdvIgnoreErr_Schultz
value-type="Double"
value-type-confirmed="true"
default-on-double="0.0"
default-on-string=""
default-off-double="0.0"
default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - GetCount
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzGetCount"
name="SchultzGetCount" // GetCount_Schultz
value-type="Double"
value-type-confirmed="true"
default-on-double="0.0"
default-on-string=""
default-off-double="0.0"
default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - ClearCount
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzClearCount" name="SchultzClearCount" // ClearCount_Schultz
value-type="Double" value-type-confirmed="true" default-on-double="0.0" default-on-string="" default-off-double="0.0" default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs" index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - GetPitch
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzGetPitch" name="SchultzGetPitch" // GetPitch_Schultz
value-type="Double" value-type-confirmed="true" default-on-double="0.0" default-on-string="" default-off-double="0.0" default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false"
coordinated-after-actuate="false"
coordinated-before-read="false"
enabled-actuation="LeaveAsIs"
homed-actuation="LeaveAsIs"
disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - TogglePitch
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzTogglePitch"
name="SchultzTogglePitch" // TogglePitch_Schultz
value-type="Double" value-type-confirmed="true" default-on-double="0.0" default-on-string="" default-off-double="0.0" default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false" coordinated-after-actuate="false" coordinated-before-read="false"
enabled-actuation="LeaveAsIs" homed-actuation="LeaveAsIs" disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
create actuators - GetStatus
<actuator
class="org.openpnp.machine.reference.ReferenceActuator"
id="actSchultzGetStatus" name="SchultzGetStatus" // GetStatus_Schultz
value-type="Double" value-type-confirmed="true" default-on-double="0.0" default-on-string="" default-off-double="0.0" default-off-string=""
interlock-actuator="false"
driver-id="DRV1685dcff7c76eec8"
coordinated-before-actuate="false" coordinated-after-actuate="false" coordinated-before-read="false"
enabled-actuation="LeaveAsIs" homed-actuation="LeaveAsIs" disabled-actuation="LeaveAsIs"
index="0">
<head-offsets units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
</actuator>
在飞达控制板的gcode驱动中添加G码
也是将官方例子整理好, 然后通过UI来添加.
<driver class="org.openpnp.machine.reference.driver.GcodeDriver"
id="DRV1685dcff7c76eec8" name="Schultz"
motion-control-type="ToolpathFeedRate"
communications="serial" connection-keep-alive="false"
units="Millimeters" max-feed-rate="1000"
backlash-offset-x="-1.0" backlash-offset-y="-1.0" backlash-offset-z="0.0" backlash-offset-r="0.0"
non-squareness-factor="0.0"
backlash-feed-rate-factor="0.1" timeout-milliseconds="5000" connect-wait-time-milliseconds="3000" visual-homing-enabled="true"
backslash-escaped-characters-enabled="false" remove-comments="true"
compress-gcode="false" logging-gcode="false" supporting-pre-move="false" using-letter-variables="true" infinity-timeout-milliseconds="60000">
<serial line-ending-type="LF" port-name="COM3" baud="115200" flow-control="RtsCts" data-bits="Eight" stop-bits="One" parity="None" set-dtr="false" set-rts="false" name="SerialPortCommunications"/>
<tcp line-ending-type="LF" ip-address="127.0.0.1" port="23" name="TcpCommunications"/>
// 以上就是一个GCode串口驱动, 说明以下G码是在飞达控制板中添加的
<simulated line-ending-type="LF"/>
<homing-fiducial-location units="Millimeters" x="0.0" y="0.0" z="0.0" rotation="0.0"/>
<detected-firmware><![CDATA[FIRMWARE_NAME: Schultz Feeder Controller, FIRMWARE_VERSION: 2.0]]></detected-firmware>
// 驱动默认的命令响应正则内容
// COMMAND_CONFIRM_REGEX 内容为 ^ok.*
<command type="COMMAND_CONFIRM_REGEX">
<text><![CDATA[^ok.*]]></text>
</command>
// COMMAND_ERROR_REGEX 内容为 ^error.*
<command type="COMMAND_ERROR_REGEX">
<text><![CDATA[^error.*]]></text>
</command>
// 动作命令对应的G码
// GetID => ACTUATOR_READ_COMMAND => M610N{IntegerValue}
<command head-mountable-id="actSchultzGetID" type="ACTUATOR_READ_COMMAND">
<text><![CDATA[M610N{IntegerValue}]]></text>
</command>
// GetID => ACTUATOR_READ_REGEX => ^ok.*ID: (?<Value>.+)
<command head-mountable-id="actSchultzGetID" type="ACTUATOR_READ_REGEX">
<text><![CDATA[^ok.*ID: (?<Value>.+)]]></text>
</command>
// PrePick => ACTUATE_DOUBLE_COMMAND => M600N{IntegerValue}
<command head-mountable-id="actSchultzPrePick" type="ACTUATE_DOUBLE_COMMAND">
<text><![CDATA[M600N{IntegerValue}]]></text>
</command>
// PostPick => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}
<command head-mountable-id="actSchultzPostPick" type="ACTUATE_DOUBLE_COMMAND">
<text><![CDATA[M601N{IntegerValue}]]></text>
</command>
// AdvIgnorErr => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}X1
<command head-mountable-id="actSchultzAdvIgnorErr" type="ACTUATE_DOUBLE_COMMAND">
<text><![CDATA[M601N{IntegerValue}X1]]></text>
</command>
// GetCount => ACTUATOR_READ_COMMAND => M603N{IntegerValue}
<command head-mountable-id="actSchultzGetCount" type="ACTUATOR_READ_COMMAND">
<text><![CDATA[M603N{IntegerValue}]]></text>
</command>
// GetCount => ACTUATOR_READ_REGEX => ^ok.*count: (?<Value>\d+).*
<command head-mountable-id="actSchultzGetCount" type="ACTUATOR_READ_REGEX">
<text><![CDATA[^ok.*count: (?<Value>\d+).*]]></text>
</command>
// ClearCount => ACTUATE_DOUBLE_COMMAND => M623N{IntegerValue}
<command head-mountable-id="actSchultzClearCount" type="ACTUATE_DOUBLE_COMMAND">
<text><![CDATA[M623N{IntegerValue}]]></text>
</command>
// GetPitch => ACTUATOR_READ_COMMAND => M608N{IntegerValue}
<command head-mountable-id="actSchultzGetPitch" type="ACTUATOR_READ_COMMAND">
<text><![CDATA[M608N{IntegerValue}]]></text>
</command>
// GetPitch => ACTUATOR_READ_REGEX => ^ok.(?<Value>.+)
<command head-mountable-id="actSchultzGetPitch" type="ACTUATOR_READ_REGEX">
<text><![CDATA[^ok.(?<Value>.+)]]></text>
</command>
// TogglePitch => ACTUATE_DOUBLE_COMMAND => M628N{IntegerValue}
<command head-mountable-id="actSchultzTogglePitch" type="ACTUATE_DOUBLE_COMMAND">
<text><![CDATA[M628N{IntegerValue}]]></text>
</command>
// GetStatus => ACTUATOR_READ_REGEX => ^ok.*Status: (?<Value>.+)
<command head-mountable-id="actSchultzGetStatus" type="ACTUATOR_READ_REGEX">
<text><![CDATA[^ok.*Status: (?<Value>.+)]]></text>
</command>
// GetStatus => ACTUATOR_READ_COMMAND => M602N{IntegerValue}
<command head-mountable-id="actSchultzGetStatus" type="ACTUATOR_READ_COMMAND">
<text><![CDATA[M602N{IntegerValue}]]></text>
</command>
</driver>
进一步整理, 将具体要在飞达控制板驱动中添加的内容整理出来.
// 驱动默认的命令响应正则内容
// COMMAND_CONFIRM_REGEX 内容为 ^ok.*
// COMMAND_ERROR_REGEX 内容为 ^error.*
// 动作命令对应的G码
// GetID => ACTUATOR_READ_COMMAND => M610N{IntegerValue}
// GetID => ACTUATOR_READ_REGEX => ^ok.*ID: (?<Value>.+)
// PrePick => ACTUATE_DOUBLE_COMMAND => M600N{IntegerValue}
// PostPick => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}
// AdvIgnorErr => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}X1
// GetCount => ACTUATOR_READ_COMMAND => M603N{IntegerValue}
// GetCount => ACTUATOR_READ_REGEX => ^ok.*count: (?<Value>\d+).*
// ClearCount => ACTUATE_DOUBLE_COMMAND => M623N{IntegerValue}
// GetPitch => ACTUATOR_READ_COMMAND => M608N{IntegerValue}
// GetPitch => ACTUATOR_READ_REGEX => ^ok.(?<Value>.+)
// TogglePitch => ACTUATE_DOUBLE_COMMAND => M628N{IntegerValue}
// GetStatus => ACTUATOR_READ_REGEX => ^ok.*Status: (?<Value>.+)
// GetStatus => ACTUATOR_READ_COMMAND => M602N{IntegerValue}
看到, 如果只是为了知道回包是对是错, 就用驱动默认的COMMAND_CONFIRM_REGEX/COMMAND_ERROR_REGEX
如果要从回包中知道对错, 还要知道错误码/计数值之类的内容, 就需要实现动作的命令ACTUATOR_READ_REGEX, 定义正则, e.g. ^ok.count: (?\d+). 如果回包是对的, 就将值从回包中拿出来用.
根据上面最终整理出来要添加的动作和命令, 向上面建立的空飞达控制板串口驱动中添加.
添加的过程没啥要记录的, 添加完的截图如下:
COMMAND_CONFIRM_REGEX 内容为 ^ok.*
COMMAND_ERROR_REGEX 内容为 ^error.*
GetID => ACTUATOR_READ_COMMAND => M610N{IntegerValue}
GetID => ACTUATOR_READ_REGEX => ^ok.*ID: (?<Value>.+)
PrePick => ACTUATE_DOUBLE_COMMAND => M600N{IntegerValue}
PostPick => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}
AdvIgnorErr => ACTUATE_DOUBLE_COMMAND => M601N{IntegerValue}X1
GetCount => ACTUATOR_READ_COMMAND => M603N{IntegerValue}
GetCount => ACTUATOR_READ_REGEX => ^ok.*count: (?<Value>\d+).*
ClearCount => ACTUATE_DOUBLE_COMMAND => M623N{IntegerValue}
GetPitch => ACTUATOR_READ_COMMAND => M608N{IntegerValue}
GetPitch => ACTUATOR_READ_REGEX => ^ok.(?<Value>.+)
TogglePitch => ACTUATE_DOUBLE_COMMAND => M628N{IntegerValue}
GetStatus => ACTUATOR_READ_COMMAND => M602N{IntegerValue}
GetStatus => ACTUATOR_READ_REGEX => ^ok.*Status: (?<Value>.+)
此时, 要保证飞达已经安装到了设备上的飞达安装位置, 已经和PC机建立了正常的串口通讯(openpnp - 二手西门子电动飞达的测试).
并且物料已经正常载入到了子飞达(openpnp - 二手西门子电动飞达 - 物料编带安装的正确姿势), 且飞达物理错误指示灯是灭的.
在openpnp中添加西门子电动飞达
西门子电动飞达有多个子飞达(e.g. 8mm x 2位, 8mm x 3位, 12mm x 2位).
在openpnp中对应的飞达类型为SlotSchultzFeeder, 为每一个子飞达都添加一个openpnp用的飞达.
为西门子飞达上面每一位子飞达都建立一个基准点封装
https://github.com/openpnp/openpnp/wiki/Fiducials#creating-a-fiducial-package
按照官方说明, 手工添加一个飞达上用的取料的基准点, 封装名字为 Fiducial_SlotSchultz
封装的单位为mm, 外形尺寸都设置为0. 我这里默认就是这样, 这就是官方要求的.
添加一个焊盘, 基准点一般就是一个焊盘就够了.
将焊盘名称改为1
将顶部相机挪到飞达的对应子飞达前部的飞达取料基准点
让飞达上的基准点孔在顶部相机十字中间.
将基准点焊盘位置X,Y改为0,0, 圆度改为100(就是一个圆形焊盘)
修改焊盘的长宽, 因为这时已经选中了基准点焊盘. 修改基准点长宽后, 在顶部相机上会有封装的预览黄圈, 让预览的黄圈准确套住飞达基准孔的形状. 我这里实验的结果是1.2mm直径的圆.
测试一下, 基准点视觉是否好使, 应该能识别到基准点图形才行.
将这个视觉专门给这个基准点用.
默认的视觉选项比较少.
整理官方给的视觉选项, 尝试添加官方推荐的选项.
官方原版的基准点视觉管道设置如下:
<entry>
<string>FIDUCIAL-FEEDER</string>
<part-settings enabled="false">
<pipeline>
<stages>
<cv-stage class="org.openpnp.vision.pipeline.stages.CreateFootprintTemplateImage" name="template" enabled="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_template" enabled="true" prefix="fidloc_template_" suffix=".png"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertColor" name="template_gray" enabled="true" conversion="Bgr2Gray"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageCapture" name="image" enabled="true" settle-first="true" count="1"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertColor" name="image_gray" enabled="true" conversion="Bgr2Gray"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.Threshold" name="2" enabled="true" threshold="190" auto="false" invert="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_original" enabled="true" prefix="fidloc_original_" suffix=".png"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.MatchTemplate" name="match_template" enabled="true" template-stage-name="template_gray" threshold="0.699999988079071" corr="0.8500000238418579" normalize="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageRecall" name="recall_image" enabled="true" image-stage-name="image"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.DrawTemplateMatches" name="draw_matches" enabled="true" template-matches-stage-name="match_template"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertModelToKeyPoints" name="results" enabled="true" model-stage-name="match_template"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.DrawKeyPoints" name="draw_keypoints" enabled="true" key-points-stage-name="results"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_results" enabled="true" prefix="fidloc_results_" suffix=".png"/>
</stages>
</pipeline>
</part-settings>
</entry>
整理后如下:
// 调试用的, 不用加
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_template" enabled="true" prefix="fidloc_template_" suffix=".png"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_results" enabled="true" prefix="fidloc_results_" suffix=".png"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageWriteDebug" name="debug_original" enabled="true" prefix="fidloc_original_" suffix=".png"/>
// 需要加的opencv选项
<cv-stage class="org.openpnp.vision.pipeline.stages.CreateFootprintTemplateImage" name="template" enabled="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertColor" name="template_gray" enabled="true" conversion="Bgr2Gray"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertColor" name="image_gray" enabled="true" conversion="Bgr2Gray"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageCapture" name="image" enabled="true" settle-first="true" count="1"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.Threshold" name="2" enabled="true" threshold="190" auto="false" invert="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.MatchTemplate" name="match_template" enabled="true"
template-stage-name="template_gray" threshold="0.699999988079071" corr="0.8500000238418579" normalize="true"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ImageRecall" name="recall_image" enabled="true" image-stage-name="image"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.DrawTemplateMatches" name="draw_matches" enabled="true" template-matches-stage-name="match_template"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.ConvertModelToKeyPoints" name="results" enabled="true" model-stage-name="match_template"/>
<cv-stage class="org.openpnp.vision.pipeline.stages.DrawKeyPoints" name="draw_keypoints" enabled="true" key-points-stage-name="results"/>
视觉这块, 添加的都是opencv选项. 编辑视觉管道, 然后添加视觉属性.
单击想要编辑参数的opencv选项, 在属性列表中看看有没有可以改的选项(有的是填写值, 有些是从下拉框中选择)
为啥添加这些opencv管道, 现在还没概念, 以后有机会再研究, 估计用默认的管道也可以.
添加后的这些官方选项截图如下:
选项总览:
第一列是Enable, 没勾上的就无效.
默认选项是7项, 只有3项有效.
将全部够了Enable的选项截图保存参考.
ImageCapture选项在默认选项中就有, 删掉重复添加的选项.
opencv处理选项有的参数是上面的选项处理的结果, 如果依赖的选项的实例名字写错了, 在该选项的提示区就有异常, 那这个opencv处理就会出错导致不生效. 我们手工改一个错误的依赖的选项实例名字看看错误现象.
所有选项的提示区都要没有错误提示才说明opencv选项生效了.
选项设置完, 退出后, 保存设置.
再测试基准点的识别, 现在是在定位孔中心画了一个小圈.
建立一个基准点元件(e.g. Part_Fiducial_SlotSchultz), 将封装选为Fiducial_SlotSchultz. 这个元件给西门子飞达SlotSchultzFeeder用.
飞达的子飞达号码的判断
从通讯协议可知, 飞达的子飞达号码不是0, 就是1, 没有其他值(这是对应的2位的飞达. e.g. 8mm x2, 如果是8mm x3, 可能子飞达号码就是0,1,2, 等有了新类型的飞达再实验不迟)
子飞达在母飞达上的位置, 可以从实际通讯协议效果来看, 正常固定飞达后, 从飞达尾部向飞达头部看, 左边是子飞达0, 右边是子飞达1.
飞达ID的约定
飞达ID可以改为任意4位数字.
现在我这把飞达只是测试用, 我就约定测试用的飞达ID, 一律改为9999.
如果不是9999, 就是正式生产的飞达.
那么就可以根据飞达ID, 知道这个飞达在设备上的安装位置.
e.g. 设备正面是1000, 设备背面是2000, 设备左面是1, 设备右边是26.
如果飞达装在设备正面, 从左往右数第6个, 那么这个飞达的ID就要实现改为1006, 然后再接入openpnp
为这个测试飞达的子飞达0添加一个openpnp飞达
先手工添加一个SlotSchultzFeeder
先建立一个BANK
每个openpnpd的飞达的数据, 都存在一个唯一的BANK中.
一旦开始添加数据, 这个BANK就不能变了, 否则我们添加的数据就会丢失, 飞达数据就无效了.
现在我们还不知道这个飞达的ID, 先不用改这个BANK的名字, 等知道飞达ID了, 就将BANK名称改为和唯一的飞达ID关联的名称, 易于管理和识别.
如果BANK列表中有测试时新建出来的没用的BANK, 删掉, 只保留已经存在的和当前正在添加的这个子飞达的BANK
将动作都选上, 这样就可以发具体动作来操作飞达控制板来间接的操作物理子飞达.
这时, 我们还不知道要操作那个飞达, 最好在添加openpnp飞达时, 就在飞达控制板上挂一个物理飞达. 等每一个物理飞达都添加完了, 再全部将所有openpnp飞达都挂到飞达控制板上.
只挂一个飞达时, 我们取飞达ID, 就能知道当前挂的飞达的具体飞达ID. 即使我们拿到一个不知道飞达ID的物理飞达, 也可以正常添加openpnp飞达.
将没用的飞达ID(不在openpnp中注册的西门子飞达)从列表中删除
填写子飞达号码为0(因为现在想设置飞达左边的子飞达)
读取当前唯一挂在飞达控制板上的一把飞达ID
点击load, 在飞达ID列表中选择9999L这把openpnp飞达.
将BANK名称和openpnp飞达名称, 都改为和子飞达ID相关的名称, 易于识别.
那么我们再看到列表中出现的杂色的名字, 就知道是没用的名称.
用动作取参数
如果步进值不对, 就改为和实际物料编带相同的步进值.
如果飞达状态不是OK, 就要求检查物理飞达是否正确载入了物料编带.
填写基准点部件名称
先去看一眼前面建立的基准点部件叫啥.
可知, 前面新建的西门子飞达基准点部件叫做 Part_Fiducial_SlotSchultz
将名字贴到UI对应的基准点名称处.
选择这个子飞达对应的物料名称, 我这里上的是测试料, 0603的. 我就选以前载入板子时的0603_10K的元件.
这个物料, 根据载入的实际板子, 可以更换选择, 但是这种料, 就应该只出现在一个飞达上, 不能有多个飞达同时都是一种料.
设置子飞达基准点的位置
将顶部相机移动到飞达基准点(孔)中心.
因为当前设置的是这个飞达的子飞达0, 那么这个基准孔就在子飞达0的前部, 唯一的一个定位孔.
定位点的Z在Z轴0点, 不用改, 旋转角度也和做定位点封装时一样, 是0度. 所以取顶部相机位置就行.
设置取料的位置
为了防止崩料, 取料位置应该为取料窗的第一颗料(离自动扒开封皮最近的那颗料).
在物料位置保存顶部相机坐标
物料的Z坐标, 需要切到到吸嘴, 自己1mm1mm的下降去量, 我已经测量过了, -23mm就挺好.
openpnp有点bug, 像这种坐标参数变了之后, 居然保存按钮是灰的.
变通的方法, 重新在下拉列表中选同样的元件(e.g. 0603_10K), 右下角的应用按钮就可以点击了.
飞达编带中物料的原始角度
因为我用散料飞达试过, 物料的进料方向, 如果是从设备背面向设备正面垂直的走, 角度就是-90.
这里, 就将物料的角度设置为-90.
让此飞达生效, 就可以让贴片任务来用了.
至此, 将实际的西门子二手飞达接入openpnp的添加工作就完成了.
其他子飞达同理. 需要注意的是, 每个子飞达的BANK必须是唯一的, 否则数据就乱了.
补充 - 轴的移动范围可以跨零点
当设备归零后, 用顶部相机到飞达的基准点和取料位置都是正常的.
但是, 用飞达上面的工具, 尝试将吸嘴落到取料窗中的料上, 想看看吸嘴能不能正常怼到待取的料上, 此时报错如下:
刚开始看这个错误提示挺蒙的.
莫非是我的Y轴坐标范围有限制? 还是啥参数没设置?
我标定设备时, Y的范围是 0 ~ -520mm. Y是不能有正值的.
相机是在N1/N2的背面, 是不是相机可以够到取料窗, 但是N1/N2移动到取料窗时, 相机就出了Y轴范围了?
验证一下.
将设备归零后, 直接移动X轴到飞达上面, 然后看看是不是吸嘴离够不到料窗?
直接移动X, 让顶部相机到达飞达料位上方, 然后1mm1mm的移动Y, 向Y=0靠拢, 出现一下报错提示时, Y差不多为0.5mm
现在去看看N1/N2离取料窗中的料还有多远?
感觉是差点, 好像有不到5mm的样子.
想了一会, 那唯一可以解决这个问题的方法, 就是将Y轴可移动范围改为负数.
去看了一下Y轴从零点还可以向负方向移动大约20mm.
那理论上, 可以尝试将Y轴改为10 ~ -530mm, 不过接触openpnp后, 一直就将(X = 0, Y = 0)作为移动的起点, 没跨过界.
有点因为未知而害怕, 这整不好会出现事故啊.
又想了一会, 如果不改Y范围, 我只有去重新做顶部相机支架, 那又得2周…
算了, 还是改Y轴从0 ~ -520, 改为 10 ~ -520, 操作的时候, 如果不对劲了, 赶紧用设备上的关机按钮给设备断电.
因为N1挪过去的时候, 报错是Y不到10, 那我这么改应该是可以的.
用JLOG面板, 将Y移动到 Y = 9.5, 然后先试试, Y > 0时, 是否可以正常归零, 试了一下, 听到Y轴的轴承座撞了一下, ++.
然后主板通讯就报错了, Y轴的位置也是在>0的位置
设备的行程紧张啊.
将设备/主板 都断电, 将贴头推到 Y = 0 ~ -520的范围内, 将设备/主板 都重新上电, 用openpnp重新归零.
我有点明白为啥设备在Y轴方向撞了, 应该是主板配置的归位方式是从坐标小到坐标大.
# 限位开关 - Y轴归零方向
beta_homing_direction home_to_max
而且归零的这个坐标方向home_to_max, 主板是靠电机方向去判断的, 如果在Y > 0时, 归零时, 还是会向Y+的方向移动, 所以会撞设备.
当Y坐标 > 0时, 归位时就会大概率撞设备.
从这里能看出, 如果是正常使用, 就使用一个象限的坐标系, 轴坐标不要跨零点才是安全的.
看来这么改, 如果在 Y > 0的位置归零, 有危险啊.
我也没招啊, 只能暂时自己记得, 归位时, 要回到设备中间的正常P点后, 再归零.
等重新做顶部相机支架后(将支架的中心距离做小点), 然后再用一个象限的坐标.
将跨零点的距离改为够用就行, 改为5吧. 担心撞设备.
吸嘴了一下元件, 能看到零件自动进料了, 但是没看到吸嘴吸取到元件.
将吸嘴落到元件上, 因为飞达安装位置变了, 昨天量的料位的Z坐标不够. 应该是我的飞达安装版不平引起的.
那就得每个西门子飞达都要量料位的Z坐标.
放一张小纸条到料上面, 逐步降低吸嘴, 量取料位的Z坐标.
将Z下降了0.8mm, 改为-23.8mm后, 吸取元件正常, 用原来做好的0603封装的视觉检测也好使.
补充 - 如果料盘摆歪了会导致送料错误
料盘摆歪, 最后导致编带受力不能被飞达拉动, 导致错误.
从openpnp中, 用飞达取状态, 也能看到错误码.
此时, 飞达上的错误指示灯也在频闪.
此时, 需要将料盘摆正, 手头进一次料, 错误消失.
另外, 如果发生这种因为编带阻力产生的错误, openpnp中定义的物料位置就变了, 需要重新捕捉保存一下.(因为手工进料, 消除错误后, 发现每次吸嘴都吸取不到物料, 才发现取物料的坐标变了, 莫非飞达因为固定不牢靠被编带阻力拉动了位置?)
补充 - 飞达的物料在编带内的旋转角度和散料飞达不一样
试贴后发现, 物料在板子上被旋转了-90度.
那么说明, 物料在飞达内的旋转角度是0度, 和物料厂家定义的旋转角度是一样的.
试了好使, 果真西门子二手飞达和散料飞达的物料角度定义不一样.
子飞达1的添加
子飞达1和子飞达0的区别就是飞达号码, 改成1就行, 其他一样.
另外, BANK要唯一的.
添加完子飞达1, 试过了好使, 截图如下:
添加一个子飞达, 5分钟不到.
备注
官方有脚本可以自动加西门子电动飞达的openpnp飞达, 不过自动添加完, 也需要自己修改(e.g. 基准点坐标, 物料坐标, 步进值).
看个人喜好, 都行.
我是比较喜欢手工弄, 细节清晰, 不容易搞错. 除非我自己写的东西, 或者我自己真搞不定, 或者成本接受不了, 我才会用工具.
备注
现在一个西门子二手飞达已经添加到了openpnp, 试过了好使. 比散料飞达好用多了, 花钱就是好.
多个飞达添加后, 一起运行时, 如果发现啥问题, 再记录. 估计还会有细节问题.