太空工程师吧 关注:36,694贴子:508,024
  • 8回复贴,共1

【太空程序员】良(keng)心(die)导弹商人的新产品

取消只看楼主收藏回复

经过长期(两晚上)的艰苦研发,本研究所终于实现了将跟踪器的传感器和陀螺仪数量降低到1个,填补了该技术领域的空白,抠门……哦不,是科技水平……国际领先……
为了保证科研进度,研发人员渴了喝水,饿了吃饭,每天都加班到困了才能睡觉。研发过程中,研发人员家属对研发工作表现出了极大的理解和支持(强行关灯、不给被子、背对研发人员不转身等),因此除了要感谢郭嘉以外还要感谢研发人员的家属。
现在跟踪器的基本组件数量已经减少至 4 个,加上反应堆也只有 5 个。
传统的跟踪器光四个感应器的能耗就超过了 100KW,远远超出了一个小型反应堆的负载能力。这款跟踪器减少了感应器数量,也就降低了整体功耗,一个小型反应堆就足以承载感应器、推进器以及天线等导弹所需的所有设备。
本款跟踪器还处于改进阶段,因此可能会有反应呆滞、翻跟头卖萌等问题,将本跟踪器用于装配实战导弹导致的一切损失,本研究所概不负责


IP属地:山东1楼2015-01-22 08:40回复
    在讲本款跟踪器之前,先简单介绍一下传统的跟踪器原理

    上面是一个跟踪器的正视图:在一个方块上,上下左右四面各安装一个感应器(T上 B下 L左 R右),感应范围除了背面为最小,其他方向均为最大。
    这样就形成如图感应范围。
    基本运行原理是:
    当T感应区感应到物体时,控制陀螺仪向上转 —— Pitch+(在此视图中就是基于X轴旋转)
    当B感应区感应到物体时,控制陀螺仪向下转 —— Pitch-
    当T和B感应区都感应到物体时,陀螺仪 Pitch 归零。
    L和R感应区原理同上,只不过控制的是 Yaw 轴(面对陀螺仪时左为正,右为负)
    也就是说当T、B、L、R同时感应到物体时,也就意味着跟踪器已经对准了目标,这 时 Pitch 和 Yaw 都归零,陀螺仪保持姿势。
    传统的跟踪器中,每个感应器各控制一个陀螺仪,每个感应器大约耗电25~6KW,一个小型反应堆100KW(小型飞船),因此需要两个反应堆才能承载这个跟踪系统的电能,下面是这种跟踪器的材料清单:
    感应器 * 4
    陀螺仪 * 4
    反应堆 * 2


    IP属地:山东7楼2015-01-22 09:22
    回复
      在解决了编程器运行频率的问题以后,通过程序切换感应器感应范围、判断目标位置已经成为可能,下图就是新跟踪器的感应器布局。

      基本原理是:
      1. 程序不断切换感应器感应方向,并将各个方向上的感应结果存到缓存中
      2. 程序根据缓存中各个方向的侦测数据,改变陀螺仪参数
      3. 没了
      这中间有很多陷阱,大多数是游戏脚本系统的问题
      比如:
      * 类构造函数中不能给成员变量初始化
      * swtich语句不能判断int类型(编译通过但运行报错)
      * 感应器反应时间较长,因此需要空转等待


      IP属地:山东9楼2015-01-22 09:44
      收起回复
        建造过程非常简单,简单到都不用我截图
        建立一个小型飞船放一个计时器、一个传感器、一个陀螺仪、一个反应堆、一个编程器,要注意的是,陀螺仪正面朝你,传感器面朝上(根据陀螺仪判断上下左右)
        计时器名字设置为"Timer Block"
        陀螺仪名字设置为“Gyroscope”
        传感器名字设置为“Sensor”
        计时器添加动作:编程器->运行
        编程器里粘贴下面代码:


        IP属地:山东10楼2015-01-22 09:55
        收起回复
          void Main() {
          InitBlockHelper();
          InitMem();
          Tracker.Run();
          WriteMem();
          BlockHelper.RunAction("Timer Block", "TriggerNow");
          }
          class Tracker{
          private const float SENSI = 60;
          private const int DELAY_TIMES = 6;
          private const string gyroName = "Gyroscope";
          private IMyGyro Gyro = (IMyGyro)BlockHelper.GetBlock(gyroName);
          private IMySensorBlock Sensor = (IMySensorBlock)BlockHelper.GetBlock("Sensor");
          //These Directions is base on the angle of view when you face to ship, not base on ship
          private string[][] sensorSets = {new string[]{"50", "50", "50", "50", "01", "50"}, //Top L R T B B F
          new string[]{"01", "50", "50", "50", "50", "50"}, //Right
          new string[]{"50", "50", "50", "50", "50", "01"}, //Bottom
          new string[]{"50", "01", "50", "50", "50", "50"}};//Left
          private int watchDirectionIndex; //0:T 1:R 2:B 3:L
          private int topDetectedCount;
          private int rightDetectedCount;
          private int bottomDetectedCount;
          private int leftDetectedCount;
          private static Tracker tracker;
          private Tracker() {}
          private void initTracker(){
          this.topDetectedCount = Mem.GetInt("topDetectedCount");
          this.rightDetectedCount = Mem.GetInt("rightDetectedCount");
          this.bottomDetectedCount = Mem.GetInt("bottomDetectedCount");
          this.leftDetectedCount = Mem.GetInt("leftDetectedCount");
          this.watchDirectionIndex = Mem.GetInt("watchDirectionIndex");
          }
          static public void Run(){
          if (tracker == null)
          tracker = new Tracker();
          if (tracker.NeedWait())
          return;
          tracker.initTracker();
          tracker.Track();
          }
          public void Track(){
          UpdateDirectionState();
          SetGyro();
          SwitchWatchDirection();
          }
          private void UpdateDirectionState(){
          if (IsSensorDetected()) {
          switch(Convert.ToString(watchDirectionIndex)) {
          case "0":
          Mem.SetInt("topDetectedCount", ++topDetectedCount);
          break;
          case "1":
          Mem.SetInt("rightDetectedCount", ++rightDetectedCount);
          break;
          case "2":
          Mem.SetInt("bottomDetectedCount", ++bottomDetectedCount);
          break;
          case "3":
          Mem.SetInt("leftDetectedCount", ++leftDetectedCount);
          break;
          }
          }
          else {
          switch(Convert.ToString(watchDirectionIndex)) {
          case "0":
          topDetectedCount = 0;
          Mem.SetInt("topDetectedCount", topDetectedCount);
          break;
          case "1":
          rightDetectedCount = 0;
          Mem.SetInt("rightDetectedCount", rightDetectedCount);
          break;
          case "2":
          bottomDetectedCount = 0;
          Mem.SetInt("bottomDetectedCount", bottomDetectedCount);
          break;
          case "3":
          leftDetectedCount = 0;
          Mem.SetInt("leftDetectedCount", leftDetectedCount);
          break;
          }
          }
          }
          private void SetGyro(){
          if (topDetectedCount > 0 && bottomDetectedCount > 0) {
          ResetXAxis();
          }
          else if (topDetectedCount > 0) {
          TurnToTop();
          }
          else if (bottomDetectedCount > 0) {
          TurnToBottom();
          }
          if (rightDetectedCount > 0 && leftDetectedCount > 0) {
          ResetYAxis();
          }
          else if (rightDetectedCount > 0) {
          TurnToRight();
          }
          else if (leftDetectedCount > 0) {
          TurnToLeft();
          }
          }
          private void SwitchWatchDirection(){
          int index = watchDirectionIndex;//Mem.GetInt("watchDirectionIndex");
          index = ++index % 4;
          Sensor.SetValue("Left", SensorSet(index, 0));
          Sensor.SetValue("Right", SensorSet(index, 1));
          Sensor.SetValue("Top", SensorSet(index, 2));
          Sensor.SetValue("Bottom", SensorSet(index, 3));
          Sensor.SetValue("Back", SensorSet(index, 4));
          Sensor.SetValue("Front", SensorSet(index, 5));
          Mem.SetInt("watchDirectionIndex", index);
          }
          private bool NeedWait(){
          int currentTimes = Mem.GetInt("currentTimes");
          if (currentTimes < DELAY_TIMES) {
          ++currentTimes;
          }
          else{
          currentTimes = 0;
          }
          Mem.SetInt("currentTimes", currentTimes);
          return currentTimes != 0;
          }
          private bool IsSensorDetected() {
          return Sensor.IsActive;
          }
          private void ResetXAxis() {
          Gyro.SetValue("Pitch", 0F);
          }
          private void ResetYAxis() {
          Gyro.SetValue("Yaw", 0F);
          }
          private void TurnToTop() {
          Gyro.SetValue("Pitch", SENSI);
          }
          private void TurnToBottom() {
          Gyro.SetValue("Pitch", -SENSI);
          }
          private void TurnToLeft() {
          Gyro.SetValue("Yaw", SENSI);
          }
          private void TurnToRight() {
          Gyro.SetValue("Yaw", -SENSI);
          }
          private float SensorSet(int index, int i) {
          return Convert.ToSingle(sensorSets[index][i]);
          }
          }


          IP属地:山东11楼2015-01-22 09:56
          回复
            void InitBlockHelper(){
            BlockHelper.Init(GridTerminalSystem);
            }
            static class BlockHelper{
            static private IMyGridTerminalSystem GridTerminalSystem;
            static public void Init(IMyGridTerminalSystem gts){
            GridTerminalSystem = gts;
            }
            static public IMyTerminalBlock GetBlock(string name) {
            IMyTerminalBlock block = GridTerminalSystem.GetBlockWithName(name);
            if (block == null)
            throw new Exception(String.Format("\"{0}\" Not Found"));
            return block;
            }
            static public void RunAction(IMyTerminalBlock block, string actionName) {
            var action = block.GetActionWithName(actionName);
            if (action == null)
            throw new Exception(String.Format("Action \"{0}\" of \"{1}\" NOT FOUND!", actionName,
            block.CustomName));
            action.Apply(block);
            }
            static public void RunAction(string blockName, string actionName) {
            RunAction(GetBlock(blockName), actionName);
            }
            static public void RunAction(IList<string> blockNames, string actionName) {
            for (int i = 0; i < blockNames.Count; ++i)
            RunAction(blockNames[i], actionName);
            }
            static public void RunAction(IList<IMyTerminalBlock> blocks, string actionName) {
            for (int i = 0; i < blocks.Count; ++i)
            RunAction(blocks[i], actionName);
            }
            static public IMyTerminalBlock FirstBlockWithPrefix(string prefix) {
            List<IMyTerminalBlock> allBlocks = GridTerminalSystem.Blocks;
            for (int i = 0; i < allBlocks.Count; ++i) {
            if (allBlocks[i].CustomName.StartsWith(prefix))
            return allBlocks[i];
            }
            throw new Exception(String.Format("FirstBlockWithPrefix: prefix \"{0}\" not found", prefix));
            }
            }
            void InitMem(){
            if (Storage == null)
            Storage = "";
            Mem.Init(Storage);
            }
            void WriteMem(){
            Mem.WriteToStr(ref Storage);
            }
            void SaveMem(){
            WriteMem();
            }
            class Mem{
            static private string itemSep = "|";
            static private string nameSep = ":";
            static private string arraySep = ",";
            static private string memStr;
            static private Dictionary<string, string> memDic;
            static public void Init(string str){
            Set(str);
            }
            static public void WriteToStr(ref string str){
            str = Get();
            }
            static public void Set(string value){
            memStr = value;
            memDic = new Dictionary<string, string>();
            string[] arr = memStr.Split(new string[]{itemSep}, StringSplitOptions.None);
            for (int i = 0; i < arr.Length; ++i) {
            if (arr[i] == "")
            continue;
            string[] nvArr = arr[i].Split(new string[]{nameSep}, StringSplitOptions.None);
            memDic[nvArr[0]] = nvArr.Length == 1 ? "" : nvArr[1];
            }
            }
            static public string Get(){
            StringBuilder sb = new StringBuilder();
            string[] keys = new string[memDic.Count];
            memDic.Keys.CopyTo(keys,0);
            for (int i = 0; i < keys.Length; ++i) {
            sb.Append(keys[i]);
            sb.Append(nameSep);
            sb.Append(memDic[keys[i]]);
            sb.Append(itemSep);
            }
            return sb.ToString();
            }
            static public string GetString(string name){
            if (!memDic.ContainsKey(name))
            memDic[name]="";
            return memDic[name];
            }
            static public int GetInt(string name){
            int value = 0;
            int.TryParse(GetString(name), out value);
            return value;
            }
            static public float GetFloat(string name){
            float value = 0;
            float.TryParse(GetString(name), out value);
            return value;
            }
            static public void SetString(string name, string value){
            memDic[name] = value;
            }
            static public void SetVar(string name, object value){
            SetString(name, Convert.ToString(value));
            }
            static public void SetInt(string name, int value){
            SetString(name, Convert.ToString(value));
            }
            static public void SetFloat(string name, float value){
            SetString(name, Convert.ToString(value));
            }
            }


            IP属地:山东12楼2015-01-22 09:58
            回复
              需要注意的是,没有动力时光靠陀螺仪旋转特别慢,你可以在后面加上各个方向的推进器,这样看起来转身更快了一点
              看样子是这样的:


              IP属地:山东15楼2015-01-22 10:18
              回复
                http://steamcommunity.com/sharedfiles/filedetails/?id=379220336
                以上是创意工坊地址:
                目前问题很突出,主要是反应太慢,因为目前是一直是顺时针检测四个方向,检测每个方向大约耗时90ms(经过测试,编程器每15ms运行一次,每次切换检测方向后要等待6轮,也就是90ms),一秒钟可以检测四个方向各两次。
                但是还是有优化思路的:
                当扫描到一个方向有物体时,停止顺时针扫描,切换为不断扫描本方向和反方向。
                这种方案每次只处理一个轴,但是这个轴的扫描频率可以提升到每秒钟五次
                //本来还想改成四面+四角检测,但是这样会导致要处理的情况剧增,效果很可能还不如只检测四面,如果再增加一个感应器提升每次处理能力就可以做到了


                IP属地:山东19楼2015-01-22 11:23
                收起回复
                  昨晚抽时间又搞了一下,发现之前反应迟缓的主要问题是间隔设置的太小了。6个间隔会导致检测异常:具体来讲就是设置的检测方向本来应该检测不到东西,但是可能是因为检测器检测频率问题,导致了检测器还留着更改检测方向前的结果。这样就出现出现了转转停停的情况。
                  把间隔次数增加到9~10,问题就基本解决了。
                  实现了19楼提到的优化思路
                  * 反应速度有所改善
                  * 翻滚情况有所改善(反应速度提高带来的正面效果)
                  * 修改了左右检测方向设置和Yaw旋转方向(以前正好两个都反了,向左检测时其实是向右检测,向左旋转时其实是向右旋转……虽然不影响使用但是容易让人迷惑)
                  * 把配置性的常量集中到一个配置类中,隔离功能代码和配置代码
                  //最近打算对辅助库函数再做一次调整,目的:一、包装脚本系统提供的所有功能;二、降低自定义类之间的耦合性,使得大部分类都可以单独拿出来用
                  优化过的跟踪器创意工坊地址:http://steamcommunity.com/sharedfiles/filedetails/?id=382278027


                  IP属地:山东26楼2015-01-28 09:32
                  收起回复