Quellcode durchsuchen

扩展下相机P0标定流程及界面交互

- 新增 CalibTechP0Mode.DownCameraMoveFindP0 枚举及多语言资源,支持“移动至下相机拍照位置”标定模式
- CalibrationInfo 增加 PutPoint 和 DownCameraPoint 属性,完善标定点管理
- FixedUpCameraFindP0ViewModel 增加示教/移动/开始校准等命令,支持完整下相机标定流程
- 优化 XAML 界面,按标定模式动态显示操作面板,提升交互体验
- 资源文件补充新词条,调整原有描述,增强多语言支持
- 代码细节优化,提升健壮性和可维护性
孝锋 徐 vor 8 Monaten
Ursprung
Commit
ec87294c95

+ 9 - 2
TeamAAS-VM/Enums/CalibTechP0Mode.cs

@@ -30,8 +30,15 @@ namespace TeamAAS_VP.Enums
         /// <summary>
         /// 通过下相机获取P0
         /// </summary>
-        [Description("通过下相机获取P0")]
+        [Description("上下相机对拍,下相机获取P0")]
         [Localization(ResourceName = "通过下相机获取P0", ResourceType = typeof(Lang))]
-        CameraFindP0 = 2
+        CameraFindP0 = 2,
+
+        /// <summary>
+        /// 通过移动至下相机拍照位置,拍照得到P0,然后放回原位
+        /// </summary>
+        [Description("通过移动至下相机拍照位置,拍照得到P0,然后放回原位")]
+        [Localization(ResourceName = "通过移动至下相机拍照位置拍照计算P0", ResourceType = typeof(Lang))]
+        DownCameraMoveFindP0 = 3
     }
 }

+ 21 - 0
TeamAAS-VM/Models/Calibration/CalibrationInfo.cs

@@ -306,6 +306,27 @@ namespace TeamAAS_VP.Models.Calibration
             set { SetProperty(ref _MarkPoint, value); }
         }
 
+        private RPoint _PutPoint;
+        /// <summary>
+        /// 移动相机时。通过下相机校准P0的标定快放置位置
+        /// </summary>
+        public RPoint PutPoint
+        {
+            get { return _PutPoint; }
+            set { SetProperty(ref _PutPoint, value); }
+        }
+
+        //
+        private RPoint _DownCameraPoint;
+        /// <summary>
+        /// 移动相机时。通过下相机校准P0的下相机拍照位置
+        /// </summary>
+        public RPoint DownCameraPoint
+        {
+            get { return _DownCameraPoint; }
+            set { SetProperty(ref _DownCameraPoint, value); }
+        }
+
 
         private ObservableCollection<RobotPixelPoint> _CalibPoints;
 

+ 19 - 1
TeamAAS-VM/Resources/Languages/Lang.Designer.cs

@@ -4794,6 +4794,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 螺丝供料器 的本地化字符串。
+        /// </summary>
+        public static string 螺丝供料器 {
+            get {
+                return ResourceManager.GetString("螺丝供料器", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 行 的本地化字符串。
         /// </summary>
@@ -5623,7 +5632,7 @@ namespace TeamAAS_VP.Resources.Languages {
         }
         
         /// <summary>
-        ///   查找类似 通过下相机获取P0 的本地化字符串。
+        ///   查找类似 上下相机对拍,下相机获取P0 的本地化字符串。
         /// </summary>
         public static string 通过下相机获取P0 {
             get {
@@ -5631,6 +5640,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 通过移动至下相机拍照位置,拍照得到P0,然后放回原位 的本地化字符串。
+        /// </summary>
+        public static string 通过移动至下相机拍照位置拍照计算P0 {
+            get {
+                return ResourceManager.GetString("通过移动至下相机拍照位置拍照计算P0", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 通道数 的本地化字符串。
         /// </summary>

+ 0 - 3
TeamAAS-VM/Resources/Languages/Lang.en.resx

@@ -1998,9 +1998,6 @@
   <data name="正在初始化光源控制器" xml:space="preserve">
     <value>Initializing Light Controller</value>
   </data>
-  <data name="通过下相机获取P0" xml:space="preserve">
-    <value>Get P0 with the camera below</value>
-  </data>
   <data name="选择下相机视觉流程" xml:space="preserve">
     <value>Select the next camera vision process</value>
   </data>

+ 0 - 3
TeamAAS-VM/Resources/Languages/Lang.ja-JP.resx

@@ -1998,9 +1998,6 @@
   <data name="正在初始化光源控制器" xml:space="preserve">
     <value>ライトコントローラーを初期化しています</value>
   </data>
-  <data name="通过下相机获取P0" xml:space="preserve">
-    <value>下のカメラでP 0をゲット</value>
-  </data>
   <data name="选择下相机视觉流程" xml:space="preserve">
     <value>次のカメラビジョンプロセスを選択してください</value>
   </data>

+ 4 - 1
TeamAAS-VM/Resources/Languages/Lang.resx

@@ -2091,7 +2091,7 @@
     <value>正在初始化光源控制器</value>
   </data>
   <data name="通过下相机获取P0" xml:space="preserve">
-    <value>通过下相机获取P0</value>
+    <value>上下相机对拍,下相机获取P0</value>
   </data>
   <data name="选择下相机视觉流程" xml:space="preserve">
     <value>选择下相机视觉流程</value>
@@ -2123,4 +2123,7 @@
   <data name="螺丝供料器" xml:space="preserve">
     <value>螺丝供料器</value>
   </data>
+  <data name="通过移动至下相机拍照位置拍照计算P0" xml:space="preserve">
+    <value>通过移动至下相机拍照位置,拍照得到P0,然后放回原位</value>
+  </data>
 </root>

+ 0 - 3
TeamAAS-VM/Resources/Languages/Lang.zh-TW.resx

@@ -1998,9 +1998,6 @@
   <data name="正在初始化光源控制器" xml:space="preserve">
     <value>正在初始化光源控制器</value>
   </data>
-  <data name="通过下相机获取P0" xml:space="preserve">
-    <value>透過下相機獲取P0</value>
-  </data>
   <data name="选择下相机视觉流程" xml:space="preserve">
     <value>選擇下相機視覺流程</value>
   </data>

+ 230 - 6
TeamAAS-VM/ViewModels/Calibration/FixedUpCameraFindP0ViewModel.cs

@@ -1,5 +1,6 @@
 using Cognex.VisionPro;
 using Cognex.VisionPro.ToolBlock;
+using MathNet.Numerics.LinearAlgebra;
 using Prism.Commands;
 using Prism.Events;
 using Prism.Ioc;
@@ -164,7 +165,7 @@ namespace TeamAAS_VP.ViewModels.Calibration
             set
             {
                 SetProperty(ref _SelectedCalibration, value);
-                if (value!=null)
+                if (value != null)
                 {
                     SelectCalibration.FixedUpCameraFindP0Info.CalibrationId = value.Id;
                     SelectCalibration.FixedUpCameraFindP0Info.CalibrationName = value.Name;
@@ -194,6 +195,25 @@ namespace TeamAAS_VP.ViewModels.Calibration
         public DelegateCommand FindP0Command =>
             _FindP0Command ?? (_FindP0Command = new DelegateCommand(ExecuteFindP0Command));
 
+        private DelegateCommand _TeachPutPosCommand;
+        public DelegateCommand TeachPutPosCommand =>
+            _TeachPutPosCommand ?? (_TeachPutPosCommand = new DelegateCommand(ExecuteTeachPutPosCommand));
+
+        private DelegateCommand _MovePutPosCommand;
+        public DelegateCommand MovePutPosCommand =>
+            _MovePutPosCommand ?? (_MovePutPosCommand = new DelegateCommand(ExecuteMovePutPosCommand));
+
+        private DelegateCommand _TeachDownCameraPosCommand;
+        public DelegateCommand TeachDownCameraPosCommand =>
+            _TeachDownCameraPosCommand ?? (_TeachDownCameraPosCommand = new DelegateCommand(ExecuteTeachDownCameraPosCommand));
+
+        private DelegateCommand _MoveDownCameraPosCommand;
+        public DelegateCommand MoveDownCameraPosCommand =>
+            _MoveDownCameraPosCommand ?? (_MoveDownCameraPosCommand = new DelegateCommand(ExecuteMoveDownCameraPosCommand));
+
+        private DelegateCommand _StartCalibP0Command;
+        public DelegateCommand StartCalibP0Command =>
+            _StartCalibP0Command ?? (_StartCalibP0Command = new DelegateCommand(ExecuteStartCalibP0Command));
 
         #endregion
 
@@ -317,14 +337,14 @@ namespace TeamAAS_VP.ViewModels.Calibration
                 MessageBox.Show("请先选择标定!");
                 return;
             }
-            var result = await CaptureAndProcess();
+            var result = await CaptureAndProcess(null);
             if (result.IsSuccess)
             {
                 if (SelectCalibration.MarkPoint == null)
                 {
                     SelectCalibration.MarkPoint = new RPoint();
                 }
-                SelectCalibration.MarkPoint.X = (float) result.X;
+                SelectCalibration.MarkPoint.X = (float)result.X;
                 SelectCalibration.MarkPoint.Y = (float)result.Y;
                 MessageBox.Show($"P0点计算成功!X:{result.X},Y:{result.Y}");
             }
@@ -334,11 +354,215 @@ namespace TeamAAS_VP.ViewModels.Calibration
             }
         }
 
+        /// <summary>
+        /// 通过下相机开始标定P0点
+        /// </summary>
+        async void ExecuteStartCalibP0Command()
+        {
+            //弹窗确认是否开始标定
+            var result = MessageBox.Show("确认通过下相机开始标定P0点?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
+            if (result != MessageBoxResult.Yes)
+            {
+                return;
+            }
+
+            try
+            {
+                // 1.移动至标定块放置位置,取标定块
+
+                //移动机器人至下相机位置
+                bool isSuccess = await Robot.CalibMotionAsync(SelectCalibration.PutPoint, 0);
+                if (!isSuccess)
+                {
+                    MessageBox.Show("移动至移动至标定块放置位置,取标定块失败!");
+                }
+                //打开吸气
+                Robot.CalibOutIO(true);
+                await Task.Delay(300);
+
+                // 2.移动至下相机位置,拍照,计算P0点
+                isSuccess = await Robot.CalibMotionAsync(SelectCalibration.DownCameraPoint, 0);
+                if (!isSuccess)
+                {
+                    MessageBox.Show("移动至下相机位置失败!");
+                }
+
+                var calibResult = await CaptureAndProcess(new double[] { SelectCalibration.DownCameraPoint.X, SelectCalibration.DownCameraPoint.Y, SelectCalibration.DownCameraPoint.U });
+                if (calibResult.IsSuccess)
+                {
+                    //工具坐标
+                    double toolX = calibResult.X;
+                    double toolY = calibResult.Y;
+
+                    //创建工具坐标
+                    ToolCoord toolCoord = new ToolCoord();
+                    toolCoord.SetTool(toolX, toolY);
+
+                    Vector<double> P10 = Vector<double>.Build.Dense(new double[] { SelectCalibration.PutPoint.X, SelectCalibration.PutPoint.Y });
+                    var p0 = toolCoord.GetToolnCoord(P10, SelectCalibration.PutPoint.U);
+
+                    //计算P0点
+                    if (SelectCalibration.MarkPoint == null)
+                    {
+                        SelectCalibration.MarkPoint = new RPoint();
+                    }
+                    SelectCalibration.MarkPoint.X = (float)p0[0];
+                    SelectCalibration.MarkPoint.Y = (float)p0[1];
+                    MessageBox.Show($"P0点计算成功!X:{calibResult.X},Y:{calibResult.Y}");
+                }
+                else
+                {
+                    MessageBox.Show("未找到P0点,请调整工件位置后重试!");
+                }
+
+                // 3.移动至标定块放置位置,放下标定块
+                isSuccess = await Robot.CalibMotionAsync(SelectCalibration.PutPoint, 0);
+                if (isSuccess)
+                {
+                    //关闭吸气
+                    Robot.CalibOutIO(false);
+                    await Task.Delay(300);
+
+                }
+                else
+                {
+                    MessageBox.Show("移动至标定块放置位置,放下标定块失败!");
+                }
+
+                // 4.Z轴上升安全高度
+                RPoint safePoint = SelectCalibration.PutPoint.Clone();
+                safePoint.Z = 0;
+                isSuccess = await Robot.CalibMotionAsync(safePoint, 0);
+                if (!isSuccess)
+                {
+                    MessageBox.Show("Z轴上升至安全高度失败!");
+                }
+
+                //完成提示
+                MessageBox.Show("通过下相机标定P0点完成!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("相机校准-通过下相机开始标定P0点时出错!", ex);
+                MessageBox.Show(ex.Message);
+            }
+
+
+        }
+
+        /// <summary>
+        /// 移动至下相机位置
+        /// </summary>
+        async void ExecuteMoveDownCameraPosCommand()
+        {
+            try
+            {
+                //弹窗确认是否移动
+                var result = MessageBox.Show("确认移动至下相机位置?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                if (result != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+                //移动机器人至下相机位置
+                bool isSuccess = await Robot.CalibMotionAsync(SelectCalibration.DownCameraPoint, 0);
+                if (isSuccess)
+                {
+                    MessageBox.Show("移动至下相机位置成功!");
+                }
+                else
+                {
+                    MessageBox.Show("移动至下相机位置失败!");
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 示教下相机位置
+        /// </summary>
+        void ExecuteTeachDownCameraPosCommand()
+        {
+            try
+            {
+                //弹窗确认是否示教
+                var result = MessageBox.Show("确认示教下相机位置?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                if (result != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+
+                //获取机器人当前位置
+                SelectCalibration.DownCameraPoint = Robot.GetRobotPos();
+                MessageBox.Show("示教下相机位置成功!");
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 移动至标定块放位置
+        /// </summary>
+        async void ExecuteMovePutPosCommand()
+        {
+            try
+            {
+                //弹窗确认是否移动
+                var result = MessageBox.Show("确认移动至标定块放位置?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                if (result != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+                //移动机器人至标定块放位置
+                bool isSuccess = await Robot.CalibMotionAsync(SelectCalibration.PutPoint, 0);
+                if (isSuccess)
+                {
+                    MessageBox.Show("移动至标定块放位置成功!");
+                }
+                else
+                {
+                    MessageBox.Show("移动至标定块放位置失败!");
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 示教标定块放位置
+        /// </summary>
+        void ExecuteTeachPutPosCommand()
+        {
+            try
+            {
+                //弹窗确认是否示教
+                var result = MessageBox.Show("确认示教标定块放位置?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                if (result != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+
+                //获取机器人当前位置
+                SelectCalibration.PutPoint = Robot.GetRobotPos();
+                MessageBox.Show("示教标定块放位置成功!");
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
         /// <summary>
         /// 相机拍照
         /// </summary>
         /// <returns></returns>
-        private async Task<(bool IsSuccess, double X, double Y, double U, int ImageWidth, int ImageHeight)> CaptureAndProcess()
+        private async Task<(bool IsSuccess, double X, double Y, double U, int ImageWidth, int ImageHeight)> CaptureAndProcess(double[] robotCoord)
         {
 
             ICogImage image = null;
@@ -359,7 +583,7 @@ namespace TeamAAS_VP.ViewModels.Calibration
                 double y = double.Parse(items[1]);
 
                 //校准转换
-                var calibResult = _calibrationServvice.ConvertPixelToPosition((x, y, 0), null, SelectedCalibration, Robot.Brand);
+                var calibResult = _calibrationServvice.ConvertPixelToPosition((x, y, 0), robotCoord, SelectedCalibration, Robot.Brand);
                 if (calibResult.IsSucceed)
                 {
                     x = calibResult.X;
@@ -399,7 +623,7 @@ namespace TeamAAS_VP.ViewModels.Calibration
             {
                 SelectCalibration.FixedUpCameraFindP0Info = new FixedUpCameraFindP0Info();
             }
-            if (ToolBlockEdit.Subject==null)
+            if (ToolBlockEdit.Subject == null)
             {
                 string prcPath = FilePath.CalibrationPath + "//" + SelectCalibration.Name + "//" + "FixedUpCameraTool.vpp";
                 if (File.Exists(prcPath))

+ 173 - 26
TeamAAS-VM/Views/Calibration/FixedUpCameraFindP0.xaml

@@ -11,6 +11,7 @@
              xmlns:vp="clr-namespace:Cognex.VisionPro.ToolBlock;assembly=Cognex.VisionPro.ToolGroup.Controls"
              xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
              xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.Calibration"
+             xmlns:enums="clr-namespace:TeamAAS_VP.Enums"
              xmlns:convert="clr-namespace:TeamAAS_VP.ValueConverter"
              xmlns:lex="http://wpflocalizeextension.codeplex.com"
              lex:LocalizeDictionary.DesignCulture="zh-CN"
@@ -168,32 +169,178 @@
                             </DataTemplate>
                         </ComboBox.ItemTemplate>
                     </ComboBox>
-
-                    <TextBlock Text="获取P0点:"
-                               FontWeight="Bold"
-                               Grid.Row="5"
-                               VerticalAlignment="Center"
-                               Grid.Column="0" />
-                    <Button Content="获取P0点"
-                            Grid.Row="5"
-                            Margin="0,10"
-                            Grid.Column="1"
-                            Command="{Binding FindP0Command}" />
-
-
-                    <TextBlock Grid.Row="6"
-                               Grid.ColumnSpan="2">
-                        <TextBlock.Text>
-                            <MultiBinding StringFormat="{}P0点 X:{0:F3} Y:{2:F3}">
-                                <Binding Path="SelectCalibration.MarkPoint.X"
-                                         TargetNullValue="0.000"
-                                         FallbackValue="0.000" />
-                                <Binding Path="SelectCalibration.MarkPoint.Y"
-                                         TargetNullValue="0.000"
-                                         FallbackValue="0.000" />
-                            </MultiBinding>
-                        </TextBlock.Text>
-                    </TextBlock>
+                    <StackPanel Grid.Row="6"
+                                Grid.ColumnSpan="2"
+                                VerticalAlignment="Center"
+                                Margin="0,10">
+                        <!--上下相机对拍获取P0-->
+                        <Grid d:Visibility="Visible">
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height=" auto" />
+                            </Grid.RowDefinitions>
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="auto" />
+                                <ColumnDefinition Width="auto" />
+                            </Grid.ColumnDefinitions>
+                            <TextBlock Text="获取P0点:"
+                                       FontWeight="Bold"
+                                       Grid.Row="0"
+                                       VerticalAlignment="Center"
+                                       Grid.Column="0" />
+                            <Button Content="获取P0点"
+                                    Grid.Row="0"
+                                    Margin="0,10"
+                                    Grid.Column="1"
+                                    Command="{Binding FindP0Command}" />
+                            <TextBlock Grid.Row="1"
+                                       Grid.ColumnSpan="2">
+                                <TextBlock.Text>
+                                    <MultiBinding StringFormat="{}P0点 X:{0:F3} Y:{1:F3}">
+                                        <Binding Path="SelectCalibration.MarkPoint.X"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.MarkPoint.Y"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                    </MultiBinding>
+                                </TextBlock.Text>
+                            </TextBlock>
+                            <Grid.Style>
+                                <Style TargetType="Grid">
+                                    <Setter Property="Visibility"
+                                            Value="Collapsed" />
+                                    <Style.Triggers>
+                                        <!-- 当 SelectedIndex == 0 时显示此面板 -->
+                                        <DataTrigger Binding="{Binding SelectCalibration.CalibTechP0Mode}"
+                                                     Value="{x:Static enums:CalibTechP0Mode.CameraFindP0}">
+                                            <Setter Property="Visibility"
+                                                    Value="Visible" />
+                                        </DataTrigger>
+                                    </Style.Triggers>
+                                </Style>
+                            </Grid.Style>
+                        </Grid>
+                        <!--通过移动至下相机拍照获取P0-->
+                        <Grid d:Visibility="Visible">
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height=" auto" />
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height=" auto" />
+                                <RowDefinition Height="auto" />
+                                <RowDefinition Height=" auto" />
+                                <RowDefinition Height=" auto" />
+                                <RowDefinition Height=" auto" />
+                            </Grid.RowDefinitions>
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="auto" />
+                                <ColumnDefinition Width="auto" />
+                                <ColumnDefinition Width="auto" />
+                            </Grid.ColumnDefinitions>
+                            <TextBlock Text="放置产品的位置:"
+                                       FontWeight="Bold"
+                                       Grid.Row="0"
+                                       VerticalAlignment="Center"
+                                       Grid.Column="0" />
+                            <Button Content="示教"
+                                    Grid.Row="0"
+                                    Margin="0,5"
+                                    Grid.Column="1"
+                                    Command="{Binding TeachPutPosCommand}" />
+                            <Button Content="移动至"
+                                    Grid.Row="0"
+                                    Margin="5,5"
+                                    Grid.Column="2"
+                                    Command="{Binding MovePutPosCommand}" />
+                            <TextBlock Grid.Row="1"
+                                       Grid.ColumnSpan="3">
+                                <TextBlock.Text>
+                                    <MultiBinding StringFormat="{}X:{0:F3} Y:{1:F3} Z:{2:F3} U:{3:F3}">
+                                        <Binding Path="SelectCalibration.PutPoint.X"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.PutPoint.Y"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.PutPoint.Z"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.PutPoint.U"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                    </MultiBinding>
+                                </TextBlock.Text>
+                            </TextBlock>
+                            <TextBlock Text="下相机拍照的位置:"
+                                       FontWeight="Bold"
+                                       Grid.Row="2"
+                                       VerticalAlignment="Center"
+                                       Grid.Column="0" />
+                            <Button Content="示教"
+                                    Grid.Row="2"
+                                    Margin="0,5"
+                                    Grid.Column="1"
+                                    Command="{Binding TeachDownCameraPosCommand}" />
+                            <Button Content="移动至"
+                                    Grid.Row="2"
+                                    Margin="5,5"
+                                    Grid.Column="2"
+                                    Command="{Binding MoveDownCameraPosCommand}" />
+                            <TextBlock Grid.Row="3"
+                                       Grid.ColumnSpan="3">
+                                <TextBlock.Text>
+                                    <MultiBinding StringFormat="{}X:{0:F3} Y:{1:F3} Z:{2:F3} U:{3:F3}">
+                                        <Binding Path="SelectCalibration.DownCameraPoint.X"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.DownCameraPoint.Y"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.DownCameraPoint.Z"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.DownCameraPoint.U"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                    </MultiBinding>
+                                </TextBlock.Text>
+                            </TextBlock>
+                            <Button Content="开始校准"
+                                    Grid.Row="4"
+                                    Margin="5,5"
+                                    
+                                    Grid.ColumnSpan="3"
+                                    Command="{Binding StartCalibP0Command}" />
+                            <TextBlock Grid.Row="5"
+                                       Grid.ColumnSpan="3">
+                                <TextBlock.Text>
+                                    <MultiBinding StringFormat="{}P0点 X:{0:F3} Y:{1:F3}">
+                                        <Binding Path="SelectCalibration.MarkPoint.X"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                        <Binding Path="SelectCalibration.MarkPoint.Y"
+                                                 TargetNullValue="0.000"
+                                                 FallbackValue="0.000" />
+                                    </MultiBinding>
+                                </TextBlock.Text>
+                            </TextBlock>
+                            <Grid.Style>
+                                <Style TargetType="Grid">
+                                    <Setter Property="Visibility"
+                                            Value="Collapsed" />
+                                    <Style.Triggers>
+                                        <!-- 当 SelectedIndex == 0 时显示此面板 -->
+                                        <DataTrigger Binding="{Binding SelectCalibration.CalibTechP0Mode}"
+                                                     Value="{x:Static enums:CalibTechP0Mode.CameraFindP0}">
+                                            <Setter Property="Visibility"
+                                                    Value="Visible" />
+                                        </DataTrigger>
+                                    </Style.Triggers>
+                                </Style>
+                            </Grid.Style>
+                        </Grid>
+                    </StackPanel>
                 </Grid>
             </Grid>