| 参数 | 说明 |
|---|---|
| Rev_Time | 圆顶转一圈的时间 (s) |
| Total | 圆顶转一圈的编码器步数 |
| Speed | 圆顶速度 = 360 / Rev_Time (°/s) |
| Steps_Per_Deg | Total / 360 |
| C0 | 启停补偿角度 (°),实测拟合得到 |
| DeadBand | 死区,建议 1° |
| MinRunTime | 电机最短可响应运行时间 (s) |
| Home_Offset | 霍尔零位对应的步数(SyncToAzimuth 修改) |
角度换算(在 PC 端完成,PLC 不需要知道 Total):
Az = (Cur_Step MOD Total) * 360 / Total
PLC 只按时间转电机,所有判断放在 PC 端。PLC 不比较步数,因此不存在 360° 附近"找不到目标步数"的问题。
两种方案共用同一套硬件(PLC + 编码器 + 霍尔硬件清零):方案 1 见第 3~7 节;方案 2(双 HSC 增量步数停机,精度更高)见第 8 节。
t = (offset − C0) / Speedt × 1.2 + 10 s 则 Abort 并报警)角度 = Speed × t + C0,反算 t = (offset − C0) / Speed,对小角度修正尤其重要。Cur_Step := Home_Offset,避免编码器漂移逐圈累积。建议固定使用同一边沿。// 霍尔上升沿清零
IF R_TRIG_Home.Q THEN Cur_Step := Home_Offset; END_IF;
// 收到命令:Dir, Run_ms
IF Cmd_Start THEN
Motor_Dir := Dir; Motor := TRUE; T_Run(IN := TRUE, PT := Run_ms);
END_IF;
IF T_Run.Q OR Cmd_Abort THEN
Motor := FALSE; T_Run(IN := FALSE);
END_IF;
Slewing := Motor;
void SlewToAzimuth(double target)
{
for (int i = 0; i < 2; i++) // 粗定位 + 一次修正(最多两次)
{
double err = target - ReadAzimuth(); // 读 PLC 步数换算成角度
err = ((err % 360) + 540) % 360 - 180; // 归一化到 [-180, 180),自动选最短方向
if (Math.Abs(err) < DeadBand) return;
int dir = err > 0 ? 1 : -1;
double t = (Math.Abs(err) - C0) / Speed; // 扣除启停补偿
if (t < MinRunTime) return;
plc.Move(dir, (int)(t * 1000));
WaitUntilStopped(timeout: t * 1.2 + 10); // 超时则 AbortSlew 并报警
}
}
ASCOM 要求 SlewToAzimuth 异步返回,上述循环需放在后台线程执行,Slewing 属性在整个过程(含修正)中保持 True。
以圆顶转速 3°/s 为例:
| 接口 | 实现 |
|---|---|
| Azimuth | 读 PLC 步数,按第 2 节公式换算 |
| SlewToAzimuth | 第 5 节逻辑,后台线程 |
| Slewing | PLC Slewing 位 或 修正流程未结束 |
| AbortSlew | 发送 Cmd_Abort,终止后台线程 |
| FindHome | 单向转动直到霍尔触发 |
| AtHome | 霍尔信号状态 |
| SyncToAzimuth | 重新计算 Home_Offset |
| Park | SlewToAzimuth(Park 角度) |
| SetupDialog | Rev_Time、Total、C0、DeadBand、MinRunTime |
HSC1 不受霍尔清零影响,PC 无需判断是否过零点;PLC 不做任何计算。整个转动只进一次中断,与脉冲频率无关。正交计数下,停机时的来回抖动正反抵消。
| 信号 | HSC0(模式 10,读角度) | HSC1(模式 9,剩余步数) |
|---|---|---|
| 编码器 A | I0.0 | I0.6(并联) |
| 编码器 B | I0.1 | I0.7(并联) |
| 霍尔复位 | I0.2 | 不接 |
| 其他信号 | 地址 |
|---|---|
| 电机运行 / 方向 | Q0.0 / Q0.1 |
| 带符号步数 PV = dir × (N − Lead)(PC 写) | VD100 |
| 方向(PC 写,1=正转) | V104.0 |
| 冗余时间(PC 写,单位 0.1 s) | VW110 |
| 启动 / 中止(PC 写) | M0.0 / M0.2 |
| 到位 / 超时标志(PC 读) | M1.0 / M1.1 |
| 当前角度计数 / 剩余步数进度(PC 读) | HC0 / HC1 |
每个输入点只能分配给一个 HSC,"共用编码器"指 A、B 线在端子上并联接两组输入。并联注意:
Network 1 // 首次扫描:HSC0 读角度(模式10,霍尔复位),HSC1 剩余步数(模式9)
LD SM0.1
MOVB 16#F8, SMB37 // HSC0: 允许, 更新CV/PV/方向, 4倍频
MOVD 0, SMD38
MOVD 16#7FFFFFFF, SMD42
HDEF 0, 10
HSC 0
HDEF 1, 9 // HSC1 模式9:A/B 正交,无复位
ENI
Network 2 // 启动沿
LD M0.0
EU
= M0.1
Network 3 // CV 清零,PV = PC 下发的带符号步数,启动
LD M0.1
MOVB 16#E0, SMB47 // HSC1: 允许, 更新CV/PV, 4倍频
MOVD 0, SMD48 // CV = 0
MOVD VD100, SMD52 // PV = dir × (N − Lead)
HSC 1
ATCH INT_0, 13 // 事件13: HSC1 CV=PV
R M1.0, 2 // 清到位 / 超时标志
S Q0.0, 1
R M0.0, 1
Network 4 // 方向
LD V104.0
= Q0.1
Network 5 // 冗余时间保护
LD Q0.0
TON T37, VW110
Network 6 // 超时或 PC 中止:停机
LD T37
O M0.2
R Q0.0, 1
DTCH 13
R M0.2, 1
Network 7 // 超时标志
LD T37
S M1.1, 1
INT_0 // 到位
LD SM0.0
R Q0.0, 1
DTCH 13
S M1.0, 1
void SlewToAzimuth(double target)
{
for (int i = 0; i < 2; i++) // 增量定位 + 可选一次修正
{
double err = ((target - ReadAzimuth()) % 360 + 540) % 360 - 180; // 最短路径
if (Math.Abs(err) < DeadBand) return;
int dir = err > 0 ? 1 : -1;
long n = (long)Math.Round(Math.Abs(err) * Total / 360.0); // 4 倍频单位,与 HSC0 一致
if (n <= Lead) return; // 小于滑行量,不动作
double time = Math.Abs(err) / Speed * 1.2 + 10; // 冗余时间
plc.Move(dir, dir * (n - Lead), time); // 写 V104.0 / VD100 / VW110(×10),置 M0.0
WaitUntilStopped(time + 5); // M1.0 到位;M1.1 超时则报警
}
}
ReadAzimuth() 与方案 1 相同:Az = (HC0 MOD Total) × 360 / Total + Home_Az(HC0 可能为负,取模用 ((a % m) + m) % m)。
dir 取反。AbortSlew 置 M0.2;SetupDialog 增加 Lead。以上 STL 按 S7-200 系统手册 HSC 用法编写,未经实机验证。输入点分配、SMB37/SMB47 各位含义、中断事件号请对照所用 CPU 手册确认。
结论:通讯采用单独线程,且只有这一个线程接触串口/TCP;多程序共享采用 ASCOM LocalServer(exe),所有客户端连到同一进程,由它独占 PLC 连接。
为什么要独立线程
Azimuth、Slewing,每次现场读 PLC 响应慢(每个往返几十 ms),多客户端还会并发冲突。结构:轮询线程 + 状态缓存 + 命令队列
Azimuth、Slewing、AtHome 等 getter 直接返回缓存值,立即返回。异常处理
DriverException,后台自动重连。S7-200 选型
InProc DLL 会被每个客户端加载进各自进程,各自打开同一串口,第二个程序必然失败。
| 方式 | 说明 | 评价 |
|---|---|---|
| InProc DLL | 每个客户端各自加载 | 不能共享,排除 |
| LocalServer(exe) | ASCOM 官方模板,COM 进程外服务,所有客户端共用一个 exe,exe 独占串口 | 推荐,兼容所有 Windows ASCOM 客户端 |
| Alpaca(HTTP REST) | 驱动内置 HTTP 服务,客户端走网络访问 | 适合跨机器/跨平台,可作为第二阶段 |
LocalServer 设计要点
Connected = false 不能关闭 B 程序仍在用的串口;计数归零才真正断开 Modbus。SlewToAzimuth 先取消正在进行的定位任务(含修正),再执行新目标;AbortSlew 来自任何客户端都立即生效。Slaving(圆顶跟随望远镜):不在驱动内实现,交给 NINA / SGP / ACP 或 ASCOM Dome Control 周期性调用 SlewToAzimuth。驱动只需保证新命令可打断旧命令,并设死区。
Connect() / Connecting 异步,适配后台通讯线程)。SlewToAzimuth、FindHome 立即返回,Slewing 在整个流程(含修正)中保持 True。第 1、2 层可先做成独立测试程序,对着 PLC 调通并标定 C0,再套 ASCOM 外壳。