diff --git a/dbm-service/src/main/java/com/devbeh/DbmProperties.java b/dbm-service/src/main/java/com/devbeh/DbmProperties.java
index 7fbc39b3269af9c62378fce9aaf1569d137c51ec..a47ea64bb0c2cc8f9a7e33fbe551b38c173e1955 100644
--- a/dbm-service/src/main/java/com/devbeh/DbmProperties.java
+++ b/dbm-service/src/main/java/com/devbeh/DbmProperties.java
@@ -716,6 +716,11 @@ public class DbmProperties
          */
         private boolean clearKbState = false;
 
+        /**
+         * Вектор нормали поверхности земли или null.
+         */
+        private NormalVector normal;
+
 
         public int getVideoId()
         {
@@ -823,5 +828,61 @@ public class DbmProperties
         {
             this.clearKbState = clearKbState;
         }
+
+
+        public NormalVector getNormal()
+        {
+            return normal;
+        }
+
+
+        public void setNormal(NormalVector normal)
+        {
+            this.normal = normal;
+        }
+    }
+
+
+    public static class NormalVector
+    {
+        private float x;
+        private float y;
+        private float z;
+
+
+        public float getX()
+        {
+            return x;
+        }
+
+
+        public void setX(float x)
+        {
+            this.x = x;
+        }
+
+
+        public float getY()
+        {
+            return y;
+        }
+
+
+        public void setY(float y)
+        {
+            this.y = y;
+        }
+
+
+        public float getZ()
+        {
+            return z;
+        }
+
+
+        public void setZ(float z)
+        {
+            this.z = z;
+        }
     }
 }
diff --git a/dbm-service/src/main/java/com/devbeh/DeviantBehaviourManagerStarter.java b/dbm-service/src/main/java/com/devbeh/DeviantBehaviourManagerStarter.java
index 9c72d5513defea431f7ab8e8213f3cf8823b7f75..f3cb78555b2bf9484358a01e89e43807e33e3a98 100644
--- a/dbm-service/src/main/java/com/devbeh/DeviantBehaviourManagerStarter.java
+++ b/dbm-service/src/main/java/com/devbeh/DeviantBehaviourManagerStarter.java
@@ -346,8 +346,8 @@ public class DeviantBehaviourManagerStarter implements InitializingBean, Disposa
             // Создание менеджера оценки расстояния до объектов
             DistanceEstimationManager distanceEstimationManager = new DistanceEstimationManager(fvpProp.getName(),
                     fvpProp.getVideoId(), fvpProp.getCameraHeightMm(), fvpProp.getFocalLengthMm(),
-                    fvpProp.getPixelSizeMm(), prop.getBootstrapServer(), prop.getUsername(), prop.getPassword(),
-                    prop.getDistanceRequestTopicName(), prop.getDistanceResponseTopicPrefix(),
+                    fvpProp.getPixelSizeMm(), fvpProp.getNormal(), prop.getBootstrapServer(), prop.getUsername(),
+                    prop.getPassword(), prop.getDistanceRequestTopicName(), prop.getDistanceResponseTopicPrefix(),
                     prop.getBoxContactPointLiftPercent(), prop.getMaxQueuedFrames(), prop.getMaxProcessingFrames(),
                     actionsManager, kbManager, perceptionVisualizationManager, pipelineStopper::stop, metricService);
 
diff --git a/dbm-service/src/main/java/com/devbeh/data/DistanceReq.java b/dbm-service/src/main/java/com/devbeh/data/DistanceReq.java
index 386cec587b8f75717a0ca272e5a6a81c62449f69..d859116dbe8ad2cb767b8c689237086c2242be43 100644
--- a/dbm-service/src/main/java/com/devbeh/data/DistanceReq.java
+++ b/dbm-service/src/main/java/com/devbeh/data/DistanceReq.java
@@ -80,6 +80,19 @@ private static final long serialVersionUID = 0L;
                 input.readMessage(com.devbeh.data.DistanceEstimationEntry.parser(), extensionRegistry));
             break;
           }
+          case 50: {
+            com.devbeh.data.Point3d.Builder subBuilder = null;
+            if (normalVector_ != null) {
+              subBuilder = normalVector_.toBuilder();
+            }
+            normalVector_ = input.readMessage(com.devbeh.data.Point3d.parser(), extensionRegistry);
+            if (subBuilder != null) {
+              subBuilder.mergeFrom(normalVector_);
+              normalVector_ = subBuilder.buildPartial();
+            }
+
+            break;
+          }
           default: {
             if (!parseUnknownField(
                 input, unknownFields, extensionRegistry, tag)) {
@@ -246,6 +259,44 @@ private static final long serialVersionUID = 0L;
     return entries_.get(index);
   }
 
+  public static final int NORMAL_VECTOR_FIELD_NUMBER = 6;
+  private com.devbeh.data.Point3d normalVector_;
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   * @return Whether the normalVector field is set.
+   */
+  @java.lang.Override
+  public boolean hasNormalVector() {
+    return normalVector_ != null;
+  }
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   * @return The normalVector.
+   */
+  @java.lang.Override
+  public com.devbeh.data.Point3d getNormalVector() {
+    return normalVector_ == null ? com.devbeh.data.Point3d.getDefaultInstance() : normalVector_;
+  }
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   */
+  @java.lang.Override
+  public com.devbeh.data.Point3dOrBuilder getNormalVectorOrBuilder() {
+    return getNormalVector();
+  }
+
   private byte memoizedIsInitialized = -1;
   @java.lang.Override
   public final boolean isInitialized() {
@@ -275,6 +326,9 @@ private static final long serialVersionUID = 0L;
     for (int i = 0; i < entries_.size(); i++) {
       output.writeMessage(5, entries_.get(i));
     }
+    if (normalVector_ != null) {
+      output.writeMessage(6, getNormalVector());
+    }
     unknownFields.writeTo(output);
   }
 
@@ -304,6 +358,10 @@ private static final long serialVersionUID = 0L;
       size += com.google.protobuf.CodedOutputStream
         .computeMessageSize(5, entries_.get(i));
     }
+    if (normalVector_ != null) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(6, getNormalVector());
+    }
     size += unknownFields.getSerializedSize();
     memoizedSize = size;
     return size;
@@ -332,6 +390,11 @@ private static final long serialVersionUID = 0L;
             other.getPixelSizeMm())) return false;
     if (!getEntriesList()
         .equals(other.getEntriesList())) return false;
+    if (hasNormalVector() != other.hasNormalVector()) return false;
+    if (hasNormalVector()) {
+      if (!getNormalVector()
+          .equals(other.getNormalVector())) return false;
+    }
     if (!unknownFields.equals(other.unknownFields)) return false;
     return true;
   }
@@ -358,6 +421,10 @@ private static final long serialVersionUID = 0L;
       hash = (37 * hash) + ENTRIES_FIELD_NUMBER;
       hash = (53 * hash) + getEntriesList().hashCode();
     }
+    if (hasNormalVector()) {
+      hash = (37 * hash) + NORMAL_VECTOR_FIELD_NUMBER;
+      hash = (53 * hash) + getNormalVector().hashCode();
+    }
     hash = (29 * hash) + unknownFields.hashCode();
     memoizedHashCode = hash;
     return hash;
@@ -506,6 +573,12 @@ private static final long serialVersionUID = 0L;
       } else {
         entriesBuilder_.clear();
       }
+      if (normalVectorBuilder_ == null) {
+        normalVector_ = null;
+      } else {
+        normalVector_ = null;
+        normalVectorBuilder_ = null;
+      }
       return this;
     }
 
@@ -546,6 +619,11 @@ private static final long serialVersionUID = 0L;
       } else {
         result.entries_ = entriesBuilder_.build();
       }
+      if (normalVectorBuilder_ == null) {
+        result.normalVector_ = normalVector_;
+      } else {
+        result.normalVector_ = normalVectorBuilder_.build();
+      }
       onBuilt();
       return result;
     }
@@ -632,6 +710,9 @@ private static final long serialVersionUID = 0L;
           }
         }
       }
+      if (other.hasNormalVector()) {
+        mergeNormalVector(other.getNormalVector());
+      }
       this.mergeUnknownFields(other.unknownFields);
       onChanged();
       return this;
@@ -1190,6 +1271,161 @@ private static final long serialVersionUID = 0L;
       }
       return entriesBuilder_;
     }
+
+    private com.devbeh.data.Point3d normalVector_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.devbeh.data.Point3d, com.devbeh.data.Point3d.Builder, com.devbeh.data.Point3dOrBuilder> normalVectorBuilder_;
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     * @return Whether the normalVector field is set.
+     */
+    public boolean hasNormalVector() {
+      return normalVectorBuilder_ != null || normalVector_ != null;
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     * @return The normalVector.
+     */
+    public com.devbeh.data.Point3d getNormalVector() {
+      if (normalVectorBuilder_ == null) {
+        return normalVector_ == null ? com.devbeh.data.Point3d.getDefaultInstance() : normalVector_;
+      } else {
+        return normalVectorBuilder_.getMessage();
+      }
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public Builder setNormalVector(com.devbeh.data.Point3d value) {
+      if (normalVectorBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        normalVector_ = value;
+        onChanged();
+      } else {
+        normalVectorBuilder_.setMessage(value);
+      }
+
+      return this;
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public Builder setNormalVector(
+        com.devbeh.data.Point3d.Builder builderForValue) {
+      if (normalVectorBuilder_ == null) {
+        normalVector_ = builderForValue.build();
+        onChanged();
+      } else {
+        normalVectorBuilder_.setMessage(builderForValue.build());
+      }
+
+      return this;
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public Builder mergeNormalVector(com.devbeh.data.Point3d value) {
+      if (normalVectorBuilder_ == null) {
+        if (normalVector_ != null) {
+          normalVector_ =
+            com.devbeh.data.Point3d.newBuilder(normalVector_).mergeFrom(value).buildPartial();
+        } else {
+          normalVector_ = value;
+        }
+        onChanged();
+      } else {
+        normalVectorBuilder_.mergeFrom(value);
+      }
+
+      return this;
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public Builder clearNormalVector() {
+      if (normalVectorBuilder_ == null) {
+        normalVector_ = null;
+        onChanged();
+      } else {
+        normalVector_ = null;
+        normalVectorBuilder_ = null;
+      }
+
+      return this;
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public com.devbeh.data.Point3d.Builder getNormalVectorBuilder() {
+      
+      onChanged();
+      return getNormalVectorFieldBuilder().getBuilder();
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    public com.devbeh.data.Point3dOrBuilder getNormalVectorOrBuilder() {
+      if (normalVectorBuilder_ != null) {
+        return normalVectorBuilder_.getMessageOrBuilder();
+      } else {
+        return normalVector_ == null ?
+            com.devbeh.data.Point3d.getDefaultInstance() : normalVector_;
+      }
+    }
+    /**
+     * <pre>
+     * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+     * </pre>
+     *
+     * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.devbeh.data.Point3d, com.devbeh.data.Point3d.Builder, com.devbeh.data.Point3dOrBuilder> 
+        getNormalVectorFieldBuilder() {
+      if (normalVectorBuilder_ == null) {
+        normalVectorBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.devbeh.data.Point3d, com.devbeh.data.Point3d.Builder, com.devbeh.data.Point3dOrBuilder>(
+                getNormalVector(),
+                getParentForChildren(),
+                isClean());
+        normalVector_ = null;
+      }
+      return normalVectorBuilder_;
+    }
     @java.lang.Override
     public final Builder setUnknownFields(
         final com.google.protobuf.UnknownFieldSet unknownFields) {
diff --git a/dbm-service/src/main/java/com/devbeh/data/DistanceReqOrBuilder.java b/dbm-service/src/main/java/com/devbeh/data/DistanceReqOrBuilder.java
index b1c6af7d6b745058aa774b1ad1e8619f747c9c20..6acc5ad40118326c22102b258e5f3d1250c36481 100644
--- a/dbm-service/src/main/java/com/devbeh/data/DistanceReqOrBuilder.java
+++ b/dbm-service/src/main/java/com/devbeh/data/DistanceReqOrBuilder.java
@@ -101,4 +101,31 @@ public interface DistanceReqOrBuilder extends
    */
   com.devbeh.data.DistanceEstimationEntryOrBuilder getEntriesOrBuilder(
       int index);
+
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   * @return Whether the normalVector field is set.
+   */
+  boolean hasNormalVector();
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   * @return The normalVector.
+   */
+  com.devbeh.data.Point3d getNormalVector();
+  /**
+   * <pre>
+   * Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+   * </pre>
+   *
+   * <code>.com.devbeh.data.Point3d normal_vector = 6;</code>
+   */
+  com.devbeh.data.Point3dOrBuilder getNormalVectorOrBuilder();
 }
diff --git a/dbm-service/src/main/java/com/devbeh/data/Messages.java b/dbm-service/src/main/java/com/devbeh/data/Messages.java
index cd13e9d305434591efe38abd3cc8ff058c1a440b..111dd763f8e70532594fd354005f286fb7032ddc 100644
--- a/dbm-service/src/main/java/com/devbeh/data/Messages.java
+++ b/dbm-service/src/main/java/com/devbeh/data/Messages.java
@@ -168,26 +168,27 @@ public final class Messages {
       "ies\030\001 \003(\0132\036.com.devbeh.data.TrackingEntr" +
       "y\"f\n\rTrackingEntry\0222\n\tdetection\030\001 \001(\0132\037." +
       "com.devbeh.data.DetectionEntry\022\n\n\002id\030\002 \001" +
-      "(\003\022\025\n\rdetection_ind\030\003 \001(\005\"\241\001\n\013DistanceRe" +
+      "(\003\022\025\n\rdetection_ind\030\003 \001(\005\"\322\001\n\013DistanceRe" +
       "q\022\r\n\005image\030\001 \001(\014\022\030\n\020camera_height_mm\030\002 \001" +
       "(\002\022\027\n\017focal_length_mm\030\003 \001(\002\022\025\n\rpixel_siz" +
       "e_mm\030\004 \001(\002\0229\n\007entries\030\005 \003(\0132(.com.devbeh" +
-      ".data.DistanceEstimationEntry\"C\n\027Distanc" +
-      "eEstimationEntry\022(\n\006points\030\001 \003(\0132\030.com.d" +
-      "evbeh.data.Point3d\"\216\001\n\014DistanceResp\022/\n\rn" +
-      "ormal_vector\030\001 \001(\0132\030.com.devbeh.data.Poi" +
-      "nt3d\0229\n\007entries\030\002 \003(\0132(.com.devbeh.data." +
-      "DistanceEstimationEntry\022\022\n\nmask_image\030\003 " +
-      "\001(\014\"]\n\nActionsReq\0223\n\007entries\030\001 \003(\0132\".com" +
-      ".devbeh.data.ActionPersonEntry\022\032\n\022window" +
-      "_size_frames\030\002 \001(\005\"I\n\021ActionPersonEntry\022" +
-      "\n\n\002id\030\001 \001(\003\022(\n\006joints\030\002 \003(\0132\030.com.devbeh" +
-      ".data.Point3d\"O\n\013ActionsResp\022(\n\007entries\030" +
-      "\001 \003(\0132\027.com.devbeh.data.Action\022\026\n\016latenc" +
-      "y_frames\030\002 \001(\005\"K\n\006Action\022\027\n\017first_person" +
-      "_id\030\001 \001(\003\022\030\n\020second_person_id\030\002 \001(\003\022\016\n\006a" +
-      "ction\030\003 \001(\005\"\030\n\tErrorResp\022\013\n\003msg\030\001 \001(\tB\002P" +
-      "\001b\006proto3"
+      ".data.DistanceEstimationEntry\022/\n\rnormal_" +
+      "vector\030\006 \001(\0132\030.com.devbeh.data.Point3d\"C" +
+      "\n\027DistanceEstimationEntry\022(\n\006points\030\001 \003(" +
+      "\0132\030.com.devbeh.data.Point3d\"\216\001\n\014Distance" +
+      "Resp\022/\n\rnormal_vector\030\001 \001(\0132\030.com.devbeh" +
+      ".data.Point3d\0229\n\007entries\030\002 \003(\0132(.com.dev" +
+      "beh.data.DistanceEstimationEntry\022\022\n\nmask" +
+      "_image\030\003 \001(\014\"]\n\nActionsReq\0223\n\007entries\030\001 " +
+      "\003(\0132\".com.devbeh.data.ActionPersonEntry\022" +
+      "\032\n\022window_size_frames\030\002 \001(\005\"I\n\021ActionPer" +
+      "sonEntry\022\n\n\002id\030\001 \001(\003\022(\n\006joints\030\002 \003(\0132\030.c" +
+      "om.devbeh.data.Point3d\"O\n\013ActionsResp\022(\n" +
+      "\007entries\030\001 \003(\0132\027.com.devbeh.data.Action\022" +
+      "\026\n\016latency_frames\030\002 \001(\005\"K\n\006Action\022\027\n\017fir" +
+      "st_person_id\030\001 \001(\003\022\030\n\020second_person_id\030\002" +
+      " \001(\003\022\016\n\006action\030\003 \001(\005\"\030\n\tErrorResp\022\013\n\003msg" +
+      "\030\001 \001(\tB\002P\001b\006proto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
@@ -276,7 +277,7 @@ public final class Messages {
     internal_static_com_devbeh_data_DistanceReq_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_com_devbeh_data_DistanceReq_descriptor,
-        new java.lang.String[] { "Image", "CameraHeightMm", "FocalLengthMm", "PixelSizeMm", "Entries", });
+        new java.lang.String[] { "Image", "CameraHeightMm", "FocalLengthMm", "PixelSizeMm", "Entries", "NormalVector", });
     internal_static_com_devbeh_data_DistanceEstimationEntry_descriptor =
       getDescriptor().getMessageTypes().get(14);
     internal_static_com_devbeh_data_DistanceEstimationEntry_fieldAccessorTable = new
diff --git a/dbm-service/src/main/java/com/devbeh/managers/DistanceEstimationManager.java b/dbm-service/src/main/java/com/devbeh/managers/DistanceEstimationManager.java
index ba60afba4fbd15ce80cfba6c1661338d62f5d8e5..25d5dbc8c810b4e250ddab9b8d675c656e83fcea 100644
--- a/dbm-service/src/main/java/com/devbeh/managers/DistanceEstimationManager.java
+++ b/dbm-service/src/main/java/com/devbeh/managers/DistanceEstimationManager.java
@@ -10,6 +10,7 @@ import java.util.concurrent.Semaphore;
 import java.util.stream.Collectors;
 
 import com.devbeh.Actor;
+import com.devbeh.DbmProperties;
 import com.devbeh.DetectionClass;
 import com.devbeh.FrameIdAndBytes;
 import com.devbeh.MetricService;
@@ -71,6 +72,11 @@ public class DistanceEstimationManager extends AbstractDistributor implements Fr
      */
     private final float pixelSizeMm;
 
+    /**
+     * Вектор нормали поверхности земли в пространстве камеры или null.
+     */
+    private final DbmProperties.NormalVector normal;
+
     /**
      * Используется для вычисления точки касания объекта с землей. Считается, что точка касания расположена посередине
      * обрамляющей рамки по оси x и на данный процент выше нижней точки рамки. Процент вычисляется относительно высоты
@@ -80,23 +86,24 @@ public class DistanceEstimationManager extends AbstractDistributor implements Fr
 
 
     public DistanceEstimationManager(String videoName, int videoId, float cameraHeightMm, float focalLengthMm,
-            float pixelSizeMm, String bootstrapServer, String username, String password, String requestTopicName,
-            String responseTopicPrefix, float boxContactPointLiftPercent, int maxQueuedRequests,
-            int maxProcessingRequests, ActionsManager am, KBManager kbm, PerceptionVisualizationManager pvm,
-            Runnable pipelineStopper, MetricService metricService)
+            float pixelSizeMm, DbmProperties.NormalVector normal, String bootstrapServer,
+            String username, String password, String requestTopicName, String responseTopicPrefix,
+            float boxContactPointLiftPercent, int maxQueuedRequests, int maxProcessingRequests, ActionsManager am,
+            KBManager kbm, PerceptionVisualizationManager pvm, Runnable pipelineStopper, MetricService metricService)
     {
-        this(videoName, videoId, cameraHeightMm, focalLengthMm, pixelSizeMm, bootstrapServer, username, password,
-                "distance-manager-" + videoId, new TopicPartition(requestTopicName, videoId),
+        this(videoName, videoId, cameraHeightMm, focalLengthMm, pixelSizeMm, normal, bootstrapServer, username,
+                password, "distance-manager-" + videoId, new TopicPartition(requestTopicName, videoId),
                 new TopicPartition(responseTopicPrefix + videoId, 0), boxContactPointLiftPercent,
                 new CompletableFuture<>(), maxQueuedRequests, maxProcessingRequests, new ReducibleSemaphore(0),
                 am, kbm, pvm, pipelineStopper, metricService);
     }
 
     private DistanceEstimationManager(String videoName, int videoId, float cameraHeightMm, float focalLengthMm,
-            float pixelSizeMm, String bootstrapServer, String username, String password, String clientId,
-            TopicPartition requestTopicPartition, TopicPartition responseTopicPartition,
-            float boxContactPointLiftPercent, CompletableFuture<Long> initialFrameIdFuture, int maxQueuedRequests,
-            int maxProcessingRequests, ReducibleSemaphore processingRequestsSem, ActionsManager am, KBManager kbm,
+            float pixelSizeMm, DbmProperties.NormalVector normal, String bootstrapServer, String username,
+            String password, String clientId, TopicPartition requestTopicPartition,
+            TopicPartition responseTopicPartition, float boxContactPointLiftPercent,
+            CompletableFuture<Long> initialFrameIdFuture, int maxQueuedRequests, int maxProcessingRequests,
+            ReducibleSemaphore processingRequestsSem, ActionsManager am, KBManager kbm,
             PerceptionVisualizationManager pvm, Runnable pipelineStopper, MetricService metricService)
     {
         super("DistanceEstimationDistributor-" + videoName, videoName + ".distance.",
@@ -115,6 +122,7 @@ public class DistanceEstimationManager extends AbstractDistributor implements Fr
         this.cameraHeightMm = cameraHeightMm;
         this.focalLengthMm = focalLengthMm;
         this.pixelSizeMm = pixelSizeMm;
+        this.normal = normal;
         this.boxContactPointLiftPercent = boxContactPointLiftPercent;
     }
 
@@ -323,6 +331,12 @@ public class DistanceEstimationManager extends AbstractDistributor implements Fr
             rb.getDistanceBuilder().setFocalLengthMm(focalLengthMm);
             rb.getDistanceBuilder().setPixelSizeMm(pixelSizeMm);
 
+            if (normal != null && (normal.getX() != 0 || normal.getY() != 0 || normal.getZ() != 0))
+            {
+                rb.getDistanceBuilder().getNormalVectorBuilder().setX(normal.getX()).setY(normal.getY())
+                        .setZ(normal.getZ());
+            }
+
             Map<Long, PoseEstimationEntry> poses = currentPoseData.getPose3D().getEntriesList().stream()
                     .collect(Collectors.toMap(PoseEstimationEntry::getTrackId, p -> p));
             for (TrackingEntryOrBuilder te : currentTrackingResult.getTrackingOrBuilder().getEntriesOrBuilderList())
diff --git a/dbm-service/src/main/java/com/devbeh/managers/PerceptionVisualizationManager.java b/dbm-service/src/main/java/com/devbeh/managers/PerceptionVisualizationManager.java
index ee878b197080876525bcda9f0172db678f8c71b3..0cd48581c202ad4b0756686234e25731d4f67066 100644
--- a/dbm-service/src/main/java/com/devbeh/managers/PerceptionVisualizationManager.java
+++ b/dbm-service/src/main/java/com/devbeh/managers/PerceptionVisualizationManager.java
@@ -513,7 +513,11 @@ public class PerceptionVisualizationManager extends Actor implements FrameConsum
         }
 
         Scalar blackColor = new Scalar(0, 0, 0, 1);
-        int i = 0;
+        Point3d normalVector = currentDistanceResult.getDistance().getNormalVector();
+        opencv_imgproc.putText(img, normalVector.getX() + " " + normalVector.getY() + " " + normalVector.getZ(),
+                new Point(10, 15), opencv_imgproc.FONT_HERSHEY_SIMPLEX, 0.4, blackColor);
+
+        int i = 1;
         for (Action a : currentActionsResult.getActions().getEntriesList())
         {
             ActionClass ac = ActionClass.findById(a.getAction());
diff --git a/dbm-service/src/main/resources/proto/messages.proto b/dbm-service/src/main/resources/proto/messages.proto
index e0dae847131e22d5dc8c0f580581469e2ae99a50..1d2da7fc4f7cdeeb44ecaf8d3822ade34d1d0465 100644
--- a/dbm-service/src/main/resources/proto/messages.proto
+++ b/dbm-service/src/main/resources/proto/messages.proto
@@ -132,6 +132,8 @@ message DistanceReq {
   // реализации компонента детектирования расстояния, переданные координаты z могут не учитываться, и детектироваться с
   // нуля. Считается, что по крайней мере одна точка касается земли
   repeated DistanceEstimationEntry entries = 5;
+  // Может использоваться для ручного задания вектора нормали, если он известен и не меняется для определенной камеры
+  Point3d normal_vector = 6;
 }
 
 message DistanceEstimationEntry {
diff --git a/kb-worker/src/main/java/com/devbeh/KBWorkerStarter.java b/kb-worker/src/main/java/com/devbeh/KBWorkerStarter.java
index a22ab5edd6f1dbb204599397aff57569e5c97fa5..dcbf50a2f84c35781b8c185d1801034654697cfa 100644
--- a/kb-worker/src/main/java/com/devbeh/KBWorkerStarter.java
+++ b/kb-worker/src/main/java/com/devbeh/KBWorkerStarter.java
@@ -184,8 +184,8 @@ public class KBWorkerStarter
 
         // Записываем знания для исправления ошибки отсутствия процедуры
         addAtomicKnowledge(Term.textToTerm("person(p, -1)"));
-        addAtomicKnowledge(Term.textToTerm("bounds(p, box(0, 0, 0, 0), -1)"));
-        addAtomicKnowledge(Term.textToTerm("position(p, point(0, 0, 0), -1)"));
+        addAtomicKnowledge(Term.textToTerm("bounds(p, box(-1000000000, -1000000000, -1000000000, -1000000000), -1)"));
+        addAtomicKnowledge(Term.textToTerm("position(p, point(-100000000, -100000000, -100000000), -1)"));
         addAtomicKnowledge(Term.textToTerm("vehicle(v, v, -1)"));
         addAtomicKnowledge(Term.textToTerm("animal(a, a, -1)"));
         addAtomicKnowledge(Term.textToTerm("bag(b, -1)"));
@@ -384,16 +384,16 @@ public class KBWorkerStarter
      */
     private static void addPersonKnowledge(KBObject kbObject, long time, List<Term> frameKnowledge)
     {
+        String objId = "p" + kbObject.getGlobalId();
         frameKnowledge.add(PERSON_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Integer(time)
         }));
 
-        addBoxAndPositionKnowledge(kbObject.getGlobalId(), kbObject.getBoundingBox(), kbObject.getPosition(), time,
-                frameKnowledge);
+        addBoxAndPositionKnowledge(objId, kbObject.getBoundingBox(), kbObject.getPosition(), time, frameKnowledge);
 
         frameKnowledge.add(LOOK_DIRECTION_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Float(kbObject.getLookDirection().getX()),
                 new org.jpl7.Float(kbObject.getLookDirection().getY()),
                 new org.jpl7.Float(kbObject.getLookDirection().getZ()),
@@ -403,7 +403,7 @@ public class KBWorkerStarter
         if (kbObject.getChild())
         {
             frameKnowledge.add(CHILD_TERM.putParams(new Term[] {
-                    new org.jpl7.Integer(kbObject.getGlobalId()),
+                    new org.jpl7.Atom(objId),
                     new org.jpl7.Integer(time)
             }));
         }
@@ -415,17 +415,18 @@ public class KBWorkerStarter
      */
     private static void addVehicleKnowledge(KBObject kbObject, String type, long time, List<Term> frameKnowledge)
     {
+        String objId = "v" + kbObject.getGlobalId();
         frameKnowledge.add(VEHICLE_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 new Atom(type),
                 new org.jpl7.Integer(time)
         }));
 
-        addBoxAndPositionKnowledge(kbObject.getGlobalId(), kbObject.getBoundingBox(), kbObject.getPosition(), time,
+        addBoxAndPositionKnowledge(objId, kbObject.getBoundingBox(), kbObject.getPosition(), time,
                 frameKnowledge);
 
         frameKnowledge.add(VOLUMETRIC_POSITION_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 Term.termArrayToList(kbObject.getVolumetricPositionList().stream()
                         .map(p -> new Compound("point", new Term[] {
                                 new org.jpl7.Float(p.getX()),
@@ -441,14 +442,14 @@ public class KBWorkerStarter
      */
     private static void addAnimalKnowledge(KBObject kbObject, String type, long time, List<Term> frameKnowledge)
     {
+        String globalId = "a" + kbObject.getGlobalId();
         frameKnowledge.add(ANIMAL_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(globalId),
                 new Atom(type),
                 new org.jpl7.Integer(time)
         }));
 
-        addBoxAndPositionKnowledge(kbObject.getGlobalId(), kbObject.getBoundingBox(), kbObject.getPosition(), time,
-                frameKnowledge);
+        addBoxAndPositionKnowledge(globalId, kbObject.getBoundingBox(), kbObject.getPosition(), time, frameKnowledge);
     }
 
 
@@ -457,36 +458,36 @@ public class KBWorkerStarter
      */
     private static void addBagKnowledge(KBObject kbObject, long time, List<Term> frameKnowledge)
     {
+        String objId = "b" + kbObject.getGlobalId();
         frameKnowledge.add(BAG_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Integer(time)
         }));
 
-        addBoxAndPositionKnowledge(kbObject.getGlobalId(), kbObject.getBoundingBox(), kbObject.getPosition(), time,
-                frameKnowledge);
+        addBoxAndPositionKnowledge(objId, kbObject.getBoundingBox(), kbObject.getPosition(), time, frameKnowledge);
     }
 
 
     private static void addPhoneKnowledge(KBObject kbObject, long time, List<Term> frameKnowledge)
     {
+        String objId = "ph" + kbObject.getGlobalId();
         frameKnowledge.add(PHONE_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(kbObject.getGlobalId()),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Integer(time)
         }));
 
-        addBoxAndPositionKnowledge(kbObject.getGlobalId(), kbObject.getBoundingBox(), kbObject.getPosition(), time,
-                frameKnowledge);
+        addBoxAndPositionKnowledge(objId, kbObject.getBoundingBox(), kbObject.getPosition(), time, frameKnowledge);
     }
 
 
     /**
      * Добавляет знания об обрамляющей рамке и положении объекта.
      */
-    private static void addBoxAndPositionKnowledge(long objId, KBBox boundingBox, KBPoint3d position, long time,
+    private static void addBoxAndPositionKnowledge(String objId, KBBox boundingBox, KBPoint3d position, long time,
             List<Term> frameKnowledge)
     {
         frameKnowledge.add(BOUNDS_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(objId),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Float(boundingBox.getTopLeft().getX()),
                 new org.jpl7.Float(boundingBox.getTopLeft().getY()),
                 new org.jpl7.Float(boundingBox.getBottomRight().getX()),
@@ -494,7 +495,7 @@ public class KBWorkerStarter
                 new org.jpl7.Integer(time)}));
 
         frameKnowledge.add(POSITION_TERM.putParams(new Term[] {
-                new org.jpl7.Integer(objId),
+                new org.jpl7.Atom(objId),
                 new org.jpl7.Float(position.getX()),
                 new org.jpl7.Float(position.getY()),
                 new org.jpl7.Float(position.getZ()),
@@ -538,8 +539,8 @@ public class KBWorkerStarter
             List<Term> frameKnowledge)
     {
         frameKnowledge.add(new Compound(started ? "started" : "finished", new Term[] {
-                new Compound(actionName, p2Id == 0 ? new Term[]{ new org.jpl7.Integer(p1Id) } :
-                        new Term[]{ new org.jpl7.Integer(p1Id), new org.jpl7.Integer(p2Id) }),
+                new Compound(actionName, p2Id == 0 ? new Term[]{ new org.jpl7.Atom("p" + p1Id) } :
+                        new Term[]{ new org.jpl7.Integer(p1Id), new org.jpl7.Atom("p" + p2Id) }),
                 new org.jpl7.Integer(time)}));
     }
 
@@ -557,7 +558,7 @@ public class KBWorkerStarter
         while (devBehDetectionsIter.hasNext())
         {
             Map.Entry<Term, Long> e = devBehDetectionsIter.next();
-            if (e.getValue() + DEV_BEH_IGNORE_PERIOD_FRAMES < LAST_FRAME_ID)
+            if (LAST_FRAME_ID - e.getValue() <= DEV_BEH_IGNORE_PERIOD_FRAMES)
             {
                 break;
             }
@@ -595,6 +596,7 @@ public class KBWorkerStarter
             Map<Long, Map<Long, DevBehFrameObject>> frameInfoMap = new HashMap<>();
             for (Term obj : devBeh.args())
             {
+                long objId = objNameToId(obj.name());
                 for (Map<String, Term> devBehObjInfo : new Query(OBJ_INFO_TERM.putParams(
                                 new Term[] { new org.jpl7.Integer(startTime), new org.jpl7.Integer(finishTime), obj })))
                 {
@@ -605,9 +607,9 @@ public class KBWorkerStarter
                     float bottomRightY = devBehObjInfo.get("BRy").floatValue();
 
                     frameInfoMap.computeIfAbsent(frameId, f -> new HashMap<>()).put(
-                            obj.longValue(),
+                            objId,
                             DevBehFrameObject.newBuilder()
-                                    .setId(obj.longValue())
+                                    .setId(objId)
                                     .setBox(KBBox.newBuilder()
                                             .setTopLeft(KBPoint2d.newBuilder()
                                                     .setX(topLeftX)
@@ -629,6 +631,29 @@ public class KBWorkerStarter
     }
 
 
+    /**
+     * Преобразует строковый идентификатор объекта с префиксом в начале в числовой идентификатор.
+     *
+     * @param name строковый идентификатор объекта
+     * @return числовой идентификатор объекта
+     */
+    private static long objNameToId(String name)
+    {
+        int i = 0;
+        while (i < name.length() && !(name.charAt(i) >= '0' && name.charAt(i) <= '9'))
+        {
+            i++;
+        }
+
+        if (i == name.length())
+        {
+            throw new RuntimeException("Illegal object id " + name);
+        }
+
+        return Long.parseLong(name.substring(i));
+    }
+
+
     private static class FrameKnowledge
     {
         /**