开源改变世界

环阳/VFDSpindle 通信鲁棒性 #689

推推 grbl 3年前 (2023-01-30) 160次浏览
打开
scottbez1 打开了这个问题 2020 年 12 月 4 日 · 4条评论
打开

环阳/VFDSpindle 通信鲁棒性#689

scottbez1 打开了这个问题 2020 年 12 月 4 日 · 4条评论

注释

环阳/VFDSpindle 通信鲁棒性 #689
赞助贡献者

首先,感谢一个非常棒的项目 – 到目前为止,我真的很享受使用它!

我刚刚开始设置我的 Huanyang VFD,并且对当前 VFDSpindle 实施的稳健性有一些潜在的担忧。

对于一些背景情况,我最初使用的是一个便宜的 RS485 模块,上面可能是仿冒的 MAX485 IC,它可以很好地传输到主轴,但在接收模式下有可怕的噪音,完全破坏了数据,以至于没有已成功收到回复。

奇怪且有点不和谐的是,尽管从未收到来自主轴的任何有效响应,但 VFDSpindle 实现仍会愉快地发送速度和主轴启动/停止命令。get_current_rpm这最初让我感到困惑,因为在我开始实施解析器并意识到我实际上根本没有从 VFD获取任何数据之前,似乎一切都与主轴控制正常工作……

真正让我失望的是,如果接收器是间歇性的而不是 100% 损坏,行为会变得更加奇怪。如果我在我的 MAX485 和 VFD 之间连接一个共享地,接收器改进到足以偶尔得到有效响应,但噪声仍然经常破坏数据——在这种配置中,我通常能够 M3 启动主轴,但随后一些噪音会破坏响应消息并触发“关键主轴 RS485 无响应”警报。警报似乎没有触发主轴停止,它还阻止了我在没有先清除警报的情况下使用 M5 关闭主轴,从安全角度来看这似乎不太好。换句话说,当 ESP32 没有接收到任何信号时,我实际上可以更好地控制主轴来自 VFD 的响应比收到间歇性响应时要多。

我已经开始重写 VFDSpindle/Huanyang 类以重组它们并合并一些更强大的往返通信检查(还没有准备好共享的代码),但我想首先讨论我计划的更改背后的一些高级概念确保我不会因为任何原因走上一条糟糕的道路:

  • 主轴的状态(配置和实际转速和状态)将被重复读取,并在以下任何情况下发出警报:
    • 如果我们没有收到响应(仅当我们之前已经收到响应,以避免如果 ESP32 在主轴之前通电时立即发出警报)
    • 如果配置的状态与我们命令的状态不匹配——这是为了防止运动在主轴因某种原因停止运行时继续运行(例如,如果有人按下了 VFD 本身的停止按钮,或者 VFD 关闭了电机由于一些错误)
    • 如果配置的 rpm 与我们命令的 rpm 不匹配(不确定何时或为什么会发生这种情况,但最好是安全的)
    • 实际 rpm 超出了命令 rpm 的固定范围(启动/停止期间除外)——我可能不会马上解决这个问题,但检查一下似乎很有用
  • 如果最近的状态读取失败,则无法打开主轴 – 这是为了防止打开我们无法听到的主轴,这是有风险的,因为我们不知道它处于什么状态
  • 如果出现上述任何主轴错误/警报,我们将不断发送停止主轴的命令,直到一个被确认——这可以说是为了防止“失控主轴”;这样一来,瞬态通信错误就不会导致“停止主轴”命令被丢弃,并且在连接恢复时不会重新发送

对上述原则的思考?我不完全确定的主要事情是错误/警报的行为,因为我对 grbl 语义仍然有些陌生 – 导致主轴自动关闭的错误是否正常/可以接受?这对我来说似乎更安全,但也许还有其他问题,比如如果你清除错误并恢复,需要重新打开它?

我也喜欢一个 slack 邀请来进一步讨论,如果可以的话 – 我的电子邮件是我在 gmail.com 的 github 用户名 – 谢谢!

环阳/VFDSpindle 通信鲁棒性 #689
赞助贡献者作者

在相关说明中,我一直在挖掘各种 Huanyang 文档来源,以更好地理解协议中不太常用的部分,并在此处收集这些说明:https ://paper.dropbox.com/doc/Huanyang-VFD- modbus 协议–BArwgZ4Tgj_dWv27riC1eFBTAQ-836X8EruAxH21PFe0Z2Um

它主要基于这个 Huanyang 手册,但我仍然计划根据我的 VFD 验证一些奇怪的地方,看看它是文档错误还是实际的奇怪行为。

到目前为止,我从实验中注意到的一些有趣的事情:

  • 您可以发送空控制数据(功能 0x03)消息以接收回控制状态,它提供当前运行状态和方向,因此这可以用作定期轮询状态的一部分
  • 我已经确定了一些描述不佳的控制状态位的含义,例如“运行”和“运行”位之间的区别(“运行”表示 VFD设置为运行,如果您立即取消设置发送停止;“运行”表示 VFD 是否正在主动驱动电机,因此在发送停止后 VFD 减速时它保持设置的时间更长)

一般来说,在我更新的实现中,我试图在我们请求的值(我称之为“配置”值)和代表当前状态的值(无论我们请求什么)之间保持明确的区别,我称之为“实际”值。

因此,例如,我的基本状态检查当前返回 4 个值:

  • 配置_rpm
  • 实际转速
  • 配置状态(顺时针、顺时针、禁用)
  • 实际状态(顺时针、顺时针、禁用)

它通过读取“Set F”状态寄存器(用于配置的 rpm)、读取“RoTT”状态寄存器(用于实际的 rpm)和写入一个空控制命令(用于配置的和实际状态)来实现这一点。

您可以在一个简单的(正向)加速、减速测试中看到这些变化:

[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
[MSG:RS485 status: rpm=12000, arpm=318, st=1, ast=1]         <-- VFD starts running - actual rpm starts increasing, and configured state and actual state are both Cw
[MSG:RS485 status: rpm=12000, arpm=1134, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=1950, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=2766, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=3582, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=4398, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=5214, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=6030, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=6846, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=7662, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=8478, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=9294, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=10110, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=10926, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=11742, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]     <-- VFD is at full speed
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=1, ast=1]
[MSG:RS485 status: rpm=12000, arpm=12000, st=0, ast=1]    <-- VFD is commanded to stop; actual rpm is still 12000 and actual state is still Cw, but configured state has changed to Disable
[MSG:RS485 status: rpm=12000, arpm=9643, st=0, ast=1]
[MSG:RS485 status: rpm=12000, arpm=6978, st=0, ast=1]
[MSG:RS485 status: rpm=12000, arpm=4272, st=0, ast=1]
[MSG:RS485 status: rpm=12000, arpm=1552, st=0, ast=1]
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]    <-- VFD has stopped; actual rpm is 0 and actual state is now Disable 
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
[MSG:RS485 status: rpm=12000, arpm=0, st=0, ast=0]
环阳/VFDSpindle 通信鲁棒性 #689

@scottbez1很高兴您找到了获得实际 RPM 的方法,这可用于主轴全速功能。有了这个,IMO 将解决你们中的一些人对稳健性的担忧。

环阳/VFDSpindle 通信鲁棒性 #689
赞助贡献者作者

到目前为止,在这方面取得了很好的进展——我有一堆基本的测试用例通过了,包括一些不会通过现有实现的测试用例(主要是围绕错误处理)。

正在进行与主要差异 (1468ff8)

重大架构变化的高级概述:

最大的变化是 VFDSpindle 抽象。VFD 任务不再是一个相对简单的命令发送器,它从队列中弹出命令并通过几次重试发送它们,但现在实际上使用通用外部 VFD 对预期的双向通信模式进行编码:

  • 它不断轮询 VFD 的状态以确保通信正常并且 VFD 处于所需配置(rpm+状态)
    • read_status
    • 的子类实现可以通过VFDSpindle 提供read_status的新的受保护助手(处理寻址、CRC 生成和响应 CRC/长度验证)发送尽可能多的命令来获取此信息send_command
  • 如果 VFD 未处于所需配置,它将重复发送必要的命令以使其进入该状态(无限期)
    • request_configuration
    • 同样,子类实现request_configuration可以根据需要发送尽可能多(或尽可能少)的命令

VFDSpindle.h 中的函数注释更详细地描述了这个更新的抽象。

实现此抽象的关键部分是 modbus“命令”队列已被“配置”队列取代。

  • 对请求的“配置”(rpm/state)的更改直接推送到队列(作为 SpindleState 和 uint32_t rpm 的结构),而不是首先转换为 modbus 命令
  • “配置”队列的大小实际上为 1,配置消息总是使用 xQueueOverwrite 推送到队列中——这意味着最新请求的配置会替换之前的所有请求
    • 如果 VFD 任务由于通信速度慢或其他原因而落后,我们实际上不想排队和“重放”任何中间状态。最好尽快跳转到当前状态,因此较新的请求会简单地替换较旧的请求
    • 队列实际上只是一种线程安全的方式,用于将请求的 rpm/状态从 grbl 主任务传递给 VFD 任务;实际执行该请求所需的一切(决定是否需要任何通信、构造 modbus 命令等)都发生在 VFD 任务中,并由基本 VFDSpindle 调解,细节(如实际消息字节)仍然委托给子类以实施。这意味着 VFD 任务可以在需要时无限期地重试,并且永远不会“丢弃”任何东西

到目前为止,我只实现了 HuanyangSpindle,但我很快就会看到 H2A(它只是在附加的 diff 中被注释掉以避免编译问题,因为它还没有更新)。除了 Huanyang 之外,这个新的抽象可能不太正确或不够通用,无法干净地支持 H2A,在这种情况下,我将继续调整我的方法。不过,我现在可以暂时搁置这次重写;我需要先完成我的 Workbee 的构建,这样我才能真正运行完整的测试程序,而不是仅仅手动发送简单的主轴 g 代码命令……

环阳/VFDSpindle 通信鲁棒性 #689
合作者

我刚刚浏览了您的评论,老实说,我必须更详细地研究一下。

我们完全同意,在使用 VFD 时,我们应该谨慎行事。事实上,这就是我坚信 RS485 是使用 VFD 的正确方式的主要原因;大多数其他与他们合作的方式都只是单向沟通。

在 Devt 分支而不是 master 分支上工作。从长远来看,这使得 PR 和/或合并变得更加容易,也许你描述的一些内容已经在那里修复了。

之所以有一个队列与实际的主轴代码分开,是因为 grbl 的设计。GRBL 需要一个基本上负责运动控制的单一非阻塞线程。这里的运动控制块也负责主轴控制,主轴速度也是其中的一部分。不幸的是,这个线程不应该被阻塞很长时间。换句话说,无论这意味着什么,对 Spindle 的调用都应该“快速”返回。

然而,命令之一是 ‘mc_dwell’ 。我一直想改变它来接受一个函数指针,然后可以用来做一个等待操作——比如——当出现问题时,或者当主轴没有达到速度时。这与您描述的安全事项有关,并且已经在我的 TODO 列表中调查了很长一段时间。

当我设计 VFDSpindle 类时,其主要目标之一是使其尽可能独立于正在使用的 VFD。周围有很多很多 VFD 协议,在我开始之前,我浏览了一些 Delta、Huanyang(他们有不同的 VFD)、我自己的 H2A 和其他一些手册。最重要的是,它们根本不支持相同的命令。有些使用频率,有些使用 rpm。有些提供实际的转速反馈(H2A),其他的只是告诉你设置了什么寄存器值(显然是 Huanyang)。无论如何,通常有一些寄存器值你可以拉出来看看它是否还活着,通常你会在正确接收到命令时得到一个 modbus 的“ok”回复。所以,这让我想到了你提到的原则:

  • 主轴的状态(配置和实际转速和状态)将被重复读取,并在以下任何情况下发出警报:
    • 如果我们没有收到响应(仅当我们之前已经收到响应,以避免如果 ESP32 在主轴之前通电时立即发出警报)

我相信情况已经如此。“无响应”标志应该可以解决这个问题。

  • 如果配置的状态与我们命令的状态不匹配——这是为了防止运动在主轴因某种原因停止运行时继续运行(例如,如果有人按下了 VFD 本身的停止按钮,或者 VFD 关闭了电机由于一些错误)

那是个很好的观点。没有想到这一点,因此没有实施。是的,如果主轴支持寄存器值或实际转速,我确实认为这是个好主意。大多数主轴应该。至于实现,请看下一点。

  • 如果配置的 rpm 与我们命令的 rpm 不匹配(不确定何时或为什么会发生这种情况,但最好是安全的)
    实际 rpm 超出命令 rpm 的固定范围(启动/停止期间除外)——我可能不会不能马上开始,但检查一下似乎很有用

VFDSpindle.cpp 中的代码(第 78-102 行)旨在在没有命令被推入队列时定期提取不同的诊断信息。这应该确保一切都按计划进行。您可以简单地在解析器中测试 rpm 值,如果不正常就让它失败。

  • 如果最近的状态读取失败,则无法打开主轴 – 这是为了防止打开我们无法听到的主轴,这是有风险的,因为我们不知道它处于什么状态

是的。pollidx == 0是启用 VFD 主轴初始化的特殊 ID。对于某些像 H2A 这样的主轴,您需要在开始之前了解一些寄存器值。只要初始化没有发生,就不能启动主轴。

  • 如果出现上述任何主轴错误/警报,我们将不断发送停止主轴的命令,直到一个被确认——这可以说是为了防止“失控主轴”;这样一来,瞬态通信错误就不会导致“停止主轴”命令被丢弃,并且在连接恢复时不会重新发送

我不确定这是否是个好主意:如果您有一个错误的主轴速度更改,它会触发主轴停止,这会造成损坏。也就是说,将某些主轴命令标记为“必需”可能是个好主意,这意味着 MAX_ATTEMPTS 检查基本上不会被触发。

dwell如果我们可以在出现问题时以某种方式制作s,那么其中一些问题可以得到解决。那基本上只会停止机器,而事情是错误的。

我会在 slack 上给你发一些消息,让我们看看什么最有效。

免费注册 在 GitHub 上加入此对话。已有帐户? 登录评论
标签
还没有
项目

还没有

发展

没有分支机构或拉取请求

3人参加
环阳/VFDSpindle 通信鲁棒性 #689环阳/VFDSpindle 通信鲁棒性 #689环阳/VFDSpindle 通信鲁棒性 #689

喜欢 (0)