using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using Modbus.Device; using System.IO.Ports; using Modbus.Extensions.Enron; using System.Collections; public class ModbusRTU : MonoBehaviour { // 串口设置 private SerialPort serial = new SerialPort() { PortName = "COM5", DataBits = 8, ReadBufferSize = 128, BaudRate = 9600, Parity = Parity.None, StopBits = StopBits.One, WriteTimeout = 1000, DtrEnable = false, RtsEnable = false, ReadTimeout = 1000 }; // 串口主设备 private ModbusSerialMaster serialMaster; private void Start() { try { if (!serial.IsOpen) serial.Open(); serialMaster = ModbusSerialMaster.CreateRtu(serial); // 启动后台线程读取数据和监听指令 StartCoroutine(ReadRegisters()); StartCoroutine(ListenForInstructions()); } catch (Exception ex) { Debug.LogError("Error opening serial port or creating master: " + ex.Message); } } private IEnumerator ReadRegisters() { while (true) { try { // 读取温湿度及位移数据 ushort[] temperature = serialMaster.ReadInputRegisters(0x01, 1); ushort[] humidity = serialMaster.ReadInputRegisters(0x02, 1); ushort[] displacement = serialMaster.ReadInputRegisters(0x03, 1); // 更新UI或模型状态 UpdateUIAndModel(temperature[0], humidity[0], displacement[0]); yield return new WaitForSeconds(1f); // 控制读取频率 } catch (Exception ex) { Debug.LogError("Error reading registers: " + ex.Message); } } } private IEnumerator ListenForInstructions() { while (true) { if (serial.IsOpen) { if (serial.BytesToRead > 0) { var instructionBytes = new byte[serial.BytesToRead]; serial.Read(instructionBytes, 0, instructionBytes.Length); string instruction = System.Text.Encoding.ASCII.GetString(instructionBytes).TrimEnd('\n', '\r'); switch (instruction.ToUpper()) { case "FORWARD": MoveModelForward(); break; case "BACKWARD": MoveModelBackward(); break; case "STOP": StopModel(); break; } } } yield return new WaitForEndOfFrame(); } } private void UpdateUIAndModel(float temperature, float humidity, float displacement) { // 更新UI ChartManager.Instance.UpdateTemperature(temperature); ChartManager.Instance.UpdateHumidity(humidity); ChartManager.Instance.UpdateDisplacement(displacement); // 更新模型状态 if (displacement > 0) MoveModelForward(); else if (displacement < 0) MoveModelBackward(); else StopModel(); } private void MoveModelForward() { // 移动模型向前 transform.Translate(Vector3.forward * Time.deltaTime); } private void MoveModelBackward() { // 移动模型向后 transform.Translate(Vector3.back * Time.deltaTime); } private void StopModel() { // 停止模型 // 可以在此处添加其他停止逻辑 } private void OnDestroy() { if (serial != null && serial.IsOpen) serial.Close(); } }优化以上代码
时间: 2025-07-11 10:45:33 浏览: 2
<think>好的,我现在需要帮用户优化他们提供的Unity ModbusRTU代码。首先,我得仔细看一下他们的代码结构,了解各部分的功能。用户提供的代码主要是通过Modbus RTU协议从串口读取数据,然后根据这些数据更新UI和模型的位置,同时还能监听指令控制模型移动。
首先,我要检查代码中可能存在的问题。比如,串口通信部分是否有异常处理不完善的地方?协程中的循环是否有性能问题?是否有重复代码可以优化?另外,代码的可读性和扩展性如何?
观察Start方法,他们在打开串口和创建ModbusMaster时用了try-catch,这很好,但异常处理可能不够详细。比如,串口打开失败后是否需要重试?或者在协程中发生异常后是否需要重新启动协程?
接下来看ReadRegisters协程。这里在一个while循环中不断读取寄存器,但每次读取后等待1秒。这里可能会有问题,如果读取时间超过1秒,或者出现异常,可能会导致协程堆积。另外,读取三个寄存器时,每次都是单独调用ReadInputRegisters,但实际上Modbus支持一次读取多个寄存器,可以合并为一个请求,减少通信次数,提高效率。
然后是ListenForInstructions协程,它在每一帧检查串口是否有数据。这里使用WaitForEndOfFrame可能不太高效,尤其是如果数据量大或者频率高的话。可以考虑调整等待时间,比如使用WaitForSeconds(0.1f)来降低检查频率,减少CPU负担。
UpdateUIAndModel方法里,根据位移值直接调用移动方法,但这里可能会和ListenForInstructions中的指令控制产生冲突。比如,如果同时有位移数据和指令到来,模型移动可能会被覆盖。需要确认业务逻辑是否需要同时支持这两种控制方式,或者是否需要优先级处理。
另外,代码中的魔数(Magic Numbers)比如寄存器地址0x01、0x02、0x03,应该用常量或枚举来替代,提高可读性和可维护性。串口配置参数如COM5、波特率等,最好放到Inspector面板中,方便用户调整,而不是硬编码在代码里。
在资源释放方面,OnDestroy中关闭了串口,但可以考虑实现IDisposable接口,或者在应用退出时确保串口正确关闭,避免资源泄漏。
线程安全也是一个需要注意的问题,因为Modbus操作可能涉及多线程,而Unity的API需要在主线程执行。当前代码使用协程,可能已经处理了这一点,但需要确认serialMaster的操作是否是线程安全的。例如,ReadInputRegisters是否应该在主线程调用,或者是否有必要使用lock来保证线程安全。
此外,代码中的异常处理可以更细致。例如,在ReadRegisters中,如果读取失败,可能需要进行重试,或者记录更详细的错误信息。同时,可以考虑加入连接状态的检测,比如定时检查串口是否断开,然后尝试重新连接。
关于性能优化,频繁的Translate可能会影响性能,尤其是在移动模型时。可以检查是否有更高效的方式更新位置,或者限制移动更新的频率。
最后,代码的可测试性和可扩展性。是否可以将其拆分为多个组件,比如Modbus通信管理器、数据处理器、运动控制器等,以便于维护和扩展。依赖注入或事件系统(如UnityEvent)可以在数据更新时触发事件,减少直接耦合。
总结一下,优化点可能包括:
1. 串口配置参数暴露到Inspector,方便调整。
2. 使用常量或枚举代替魔数。
3. 合并Modbus读取请求,减少通信次数。
4. 优化协程的等待策略,减少CPU占用。
5. 改善异常处理和重连机制。
6. 解决可能的线程安全问题。
7. 分离关注点,提高代码模块化。
8. 加入连接状态监测和自动重连。
9. 优化资源释放,确保串口正确关闭。
10. 使用事件系统降低耦合度。
接下来需要逐步实现这些优化,并确保每一步的修改不会引入新的问题。同时,要测试修改后的代码,确认功能正常,性能提升。</think>我将分步骤为您优化这段Unity Modbus通信代码:
1. **配置参数暴露到Inspector**
```csharp
[Header("串口配置")]
[SerializeField] private string portName = "COM5";
[SerializeField] private int baudRate = 9600;
[SerializeField] private int readInterval = 1000; // 毫秒
[Header("Modbus寄存器")]
[SerializeField] private byte slaveAddress = 0x01;
[SerializeField] private ushort tempRegister = 0x01;
[SerializeField] private ushort humidityRegister = 0x02;
[SerializeField] private ushort displacementRegister = 0x03;
```
2. **增加自动重连机制**
```csharp
private bool isConnected = false;
private const int RECONNECT_INTERVAL = 3000; // 重试间隔3秒
private IEnumerator ConnectionMonitor()
{
while (true)
{
if (!isConnected && !serial.IsOpen)
{
try
{
serial.Open();
serialMaster = ModbusSerialMaster.CreateRtu(serial);
isConnected = true;
StartCoroutine(ReadRegisters());
StartCoroutine(ListenForInstructions());
}
catch (Exception ex)
{
Debug.LogWarning($"连接失败: {ex.Message}");
}
}
yield return new WaitForSeconds(RECONNECT_INTERVAL / 1000f);
}
}
```
3. **优化寄存器读取(批量读取)**
```csharp
private IEnumerator ReadRegisters()
{
const ushort REGISTER_COUNT = 3; // 同时读取温湿度+位移
while (isConnected)
{
try
{
ushort[] results = serialMaster.ReadInputRegisters(
slaveAddress,
tempRegister,
REGISTER_COUNT
);
UpdateUIAndModel(
results[0],
results[1],
results[2]
);
yield return new WaitForSeconds(readInterval / 1000f);
}
catch (Exception ex)
{
Debug.LogError($"读取失败: {ex.Message}");
isConnected = false;
yield break;
}
}
}
```
4. **指令监听优化(增加数据缓冲区)**
```csharp
private readonly Queue<string> commandQueue = new Queue<string>();
private readonly StringBuilder receiveBuffer = new StringBuilder();
private IEnumerator ListenForInstructions()
{
while (isConnected)
{
if (serial.BytesToRead > 0)
{
byte[] buffer = new byte[serial.BytesToRead];
serial.Read(buffer, 0, buffer.Length);
receiveBuffer.Append(Encoding.ASCII.GetString(buffer));
// 处理完整指令
string data = receiveBuffer.ToString();
if (data.Contains('\n'))
{
string[] commands = data.Split('\n');
for (int i = 0; i < commands.Length - 1; i++)
{
commandQueue.Enqueue(commands[i].Trim());
}
receiveBuffer.Clear();
receiveBuffer.Append(commands.Last());
}
}
// 处理队列中的指令
while (commandQueue.Count > 0)
{
ProcessCommand(commandQueue.Dequeue());
}
yield return new WaitForSeconds(0.1f); // 降低检测频率
}
}
```
5. **增加数据校验机制**
```csharp
private bool ValidateData(ushort[] data)
{
if (data == null || data.Length < 3) return false;
// 温度校验(假设范围0-100℃)
if (data[0] > 10000) return false; // 假设数据已放大100倍
// 湿度校验(0-100%)
if (data[1] > 10000) return false;
return true;
}
```
6. **线程安全优化**
```csharp
private object modbusLock = new object();
private ushort[] ReadRegistersThreadSafe(byte address, ushort start, ushort count)
{
lock (modbusLock)
{
try
{
return serialMaster.ReadInputRegisters(address, start, count);
}
catch
{
isConnected = false;
return null;
}
}
}
```
7. **完整优化后的类结构**
```csharp
public class ModbusRTU : MonoBehaviour
{
// [原有配置字段...]
private void Start()
{
serial.PortName = portName;
serial.BaudRate = baudRate;
StartCoroutine(ConnectionMonitor());
}
private void ProcessCommand(string cmd)
{
// [原有switch逻辑...]
}
private void OnApplicationQuit()
{
StopAllCoroutines();
if (serial != null && serial.IsOpen)
{
serial.Close();
serial.Dispose();
}
}
}
```
**优化亮点说明**:
1. 模块化配置:通过序列化字段实现参数可视化配置
2. 连接可靠性:增加自动重连和心跳监测机制
3. 性能提升:批量读取寄存器减少通信次数
4. 数据完整性:增加校验机制和缓冲区管理
5. 线程安全:通过lock关键字保证Modbus操作原子性
6. 异常处理:完善断开连接后的状态管理
7. 资源管理:确保应用退出时正确释放串口资源
建议在实际使用中:
1. 通过Unity Inspector设置具体通信参数
2. 添加异常处理回调接口
3. 根据实际传感器协议调整校验规则
4. 使用Modbus数据转换器处理原始数据(如IEEE754浮点转换)
阅读全文
相关推荐






