ASCOM Dome 驱动设计(PLC + 编码器 + 霍尔零位)

1. 系统组成

2. PC 端保存的参数

参数 说明
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

3. 方案 1:按时间转动 + 一次编码器修正

PLC 只按时间转电机,所有判断放在 PC 端。PLC 不比较步数,因此不存在 360° 附近"找不到目标步数"的问题。

两种方案共用同一套硬件(PLC + 编码器 + 霍尔硬件清零):方案 1 见第 3~7 节;方案 2(双 HSC 增量步数停机,精度更高)见第 8 节。

流程

  1. PC 读取当前角度 Cur,计算 offset = 目标 − 当前,并选方向(offset > 180° 时换向,offset = 360 − offset)
  2. 发送命令:方向 + 运行时间 t = (offset − C0) / Speed
  3. PLC 按时间转动后停机,PC 等待 Slewing = False(超时 t × 1.2 + 10 s 则 Abort 并报警)
  4. PC 再读一次角度,计算残差 err;若 |err| > DeadBand,按同样方法再转一次
  5. 修正后仍超出死区,则报警,不再继续转

注意事项

4. PLC 端逻辑(ST)

// 霍尔上升沿清零
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;

5. PC 端 SlewToAzimuth 核心逻辑(C#)

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。

6. 精度估算

以圆顶转速 3°/s 为例:

7. ASCOM 接口实现要点

接口 实现
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

8. 方案 2:双 HSC 增量步数停机

8.1 原理

HSC1 不受霍尔清零影响,PC 无需判断是否过零点;PLC 不做任何计算。整个转动只进一次中断,与脉冲频率无关。正交计数下,停机时的来回抖动正反抵消。

8.2 接线(S7-200 CPU 224/226)

信号 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 线在端子上并联接两组输入。并联注意:

8.3 PLC 程序(STL)

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

8.4 PC 端 SlewToAzimuth 核心逻辑(C#)

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)。

8.5 精度与注意事项

8.6 型号差异

以上 STL 按 S7-200 系统手册 HSC 用法编写,未经实机验证。输入点分配、SMB37/SMB47 各位含义、中断事件号请对照所用 CPU 手册确认。

9. ASCOM 软件架构(采用方案 1)

结论:通讯采用单独线程,且只有这一个线程接触串口/TCP;多程序共享采用 ASCOM LocalServer(exe),所有客户端连到同一进程,由它独占 PLC 连接。

9.1 Modbus 通讯层

为什么要独立线程

结构:轮询线程 + 状态缓存 + 命令队列

异常处理

S7-200 选型

9.2 多程序共享

InProc DLL 会被每个客户端加载进各自进程,各自打开同一串口,第二个程序必然失败。

方式 说明 评价
InProc DLL 每个客户端各自加载 不能共享,排除
LocalServer(exe) ASCOM 官方模板,COM 进程外服务,所有客户端共用一个 exe,exe 独占串口 推荐,兼容所有 Windows ASCOM 客户端
Alpaca(HTTP REST) 驱动内置 HTTP 服务,客户端走网络访问 适合跨机器/跨平台,可作为第二阶段

LocalServer 设计要点

Slaving(圆顶跟随望远镜):不在驱动内实现,交给 NINA / SGP / ACP 或 ASCOM Dome Control 周期性调用 SlewToAzimuth。驱动只需保证新命令可打断旧命令,并设死区。

9.3 ASCOM 接口相关

9.4 分层

  1. Modbus 通讯层:独立线程,连接、轮询、命令队列、重连
  2. 设备模型层:状态缓存、角度换算、定位流程(后台任务)
  3. ASCOM 接口层:参数校验和转发,不含通讯逻辑
  4. LocalServer 外壳:COM 注册、多客户端共享、连接引用计数
  5. (可选)Alpaca 层:复用第 2 层,对外提供 HTTP 接口

第 1、2 层可先做成独立测试程序,对着 PLC 调通并标定 C0,再套 ASCOM 外壳。